My Photo

« July 2012 | Main | September 2012 »

August 29, 2012

JNI その2:ネイティブコードからJavaメソッドを呼び出す

前記事で参考にしたのネイティブコードからJavaのメソッドを呼ぶサンプルコードが
Javaからネイティブコードを呼んでそこからさらにJavaメソッドを呼ぶ
というなんとも妙もので、実際に動かす例としてはイマイチと思った。
参考にならなそうなので、WEBでJNI関係の記事を検索して主に参考ページ1を参考にした。というか、ほぼそのまま。ただ、C++はわからないのでCでコンパイルできるようにネイティブのコードを少し変えた。

前記事と同じくWindows上で実施。ボーランドC++コンパイラを使用。
作業フォルダはC:\work。
下記のフォルダ、ファイルのツリー表示で括弧で囲ってあるファイルはツール(javac、javah、bcc32、implib)が出力するファイル。

作業フォルダ
│  JNIHello2.c
│  [JNIHello2.exe]
│  [JNIHello2.obj]
│  [JNIHello2.tds]
│  [jvm_bcc32.lib]
│
└─jp
    └─jnisample
            [JNIHello2.class]
            JNIHello2.java

  1. Javaのコードを作成
  2. DLLからlibを作成
  3. ネイティブコードを作成し実行ファイルの作成
  4. 実行

Javaコードの作成

Javaのクラス、メソッドはネイティブから呼び出すからといって特別なことは特にない。
ただ、staticメソッドの方がクラスを生成しなくて言い分簡単なのでこの例ではstaticメソッドにした。
参考ページ1のJavaコードとの違いは、パッケージをデフォルトパッケージから適当に作ったパッケージ(jp.jnisample)に変えただけ。

package jp.jnisample;

public class JNIHello2 {
	public static String getMessage(){
		return "Hello World";
	}
}

作業フォルダでjavacコマンドを実行しコードをコンパイルする。

C:\work>javac jp\jnisample\JNIHello2.java

DLLからlibを作成

参考ページ1によると、VCならばJava付属のjvm.dllを使えばよいが、ボーランドC++コンパイラの場合は jvm.dll からボーランドC++コンパイラ付属のimplibというツールを使ってlibファイルを作成しそれを使わないといけないそうだ。
下記のようにして jvm_bcc32.lib というlibファイルを作った。

C:\work>implib jvm_bcc32.lib "c:\Program Files\Java\jdk1.6.0_24\jre\bin\client\jvm.dll"

Borland Implib Version 3.0.22 Copyright (c) 1991, 2000 Inprise Corporation


ネイティブコードを作成し実行ファイルの作成

はじめの方に書いたが、参考ページ1のネイティブのコードはC++で書かれていて、自分はC++がわからない。よってCのコードとしてコンパイルできるように、ファイル名の拡張子をcに変更し、内容にも若干の修正をした。
#拡張子がcかcppかでボーランドC++コンパイラはC/C++の判定をしているんだろうか? ネイティブコードの修正点は修正点は以下。

  • 変数宣言の場所を変えた。C++ではどこでも変数宣言できるが、Cはブロックの先頭で宣言しなければならない。
  • ポインタ変数使い方。正直、忘れてる。とりあえず「env->」と書いてあった場所を「(*env)->」に書き換えた。
  • JNI関数を呼ぶときに引数を足した。(*env)->で呼ばれる関数にはenvを、(*vm)->で呼ばれる関数にはvmを第1引数として加えた。
#include <stdio.h>
#include <jni.h>

int main(){
	JNIEnv *env;
	JavaVM *vm;
	JavaVMInitArgs vm_args;
	JavaVMOption options[1];	//JVMオプション配列
	
	int res;
	jclass cls;
	jmethodID mid;
	jstring str;
	
//	options[0].optionString = "-Djava.class.path=.\\classes";	/* クラスパス */
	options[0].optionString = "-Djava.class.path=.";	/* クラスパス */

	vm_args.version = JNI_VERSION_1_6;
	vm_args.options = options;
	vm_args.nOptions = 1;    //JVMオプション個数
	vm_args.ignoreUnrecognized = 1;
//	vm_args.ignoreUnrecognized = TRUE;	// TRUEだとwindows.hのインクルードが必要になる

	//JVMの作成
	res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);

	if (res < 0) {
		printf("Fail to create JVM.\n");
		return(1);
	}
	//JNIHello2.classの取得(クラスパスより検索)
	cls = (*env)->FindClass(env, "jp/jnisample/JNIHello2");
	if (cls == 0) {
		printf("Fail to find class JNIHello2\n");
		return(1);
	}

	//取得したクラスからgetMessageというstaticメソッドを取得
	mid = (*env)->GetStaticMethodID(
			env, cls, "getMessage",
			"()Ljava/lang/String;");
	//メソッドを実行し、戻り値Stringを受け取る。
	str = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
	//UTFのchar配列に変換後、コンソールに関数の戻り値を出力。
	printf("[%s]\n", (*env)->GetStringUTFChars(env, str, NULL));

	(*vm)->DestroyJavaVM(vm);

	return 0;
}

これをコンパイル、リンクして実行ファイルを作成。

C:\work>bcc32 -L jvm_bcc32.lib JNIHello2.c
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
JNIHello2.c:
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland


実行

実行するにはパスを追加しなければならない。自分の環境では
C:\Program Files\Java\jdk1.6.0_24\jre\bin\client
を環境変数PATHに追加。
これは、さきほど jvm_bcc32.lib を作るのに使った jvm.dll のある場所にパスが通っていないといけないということだろうか?

パスを追加すると、下記のように実行できた。

C:\work>JNIHello2.exe
[Hello World]

参考ページ:
1.civic site >> ネイティブからJavaを呼び出す
2.JNI (Java Native Interface)
3.C/C++からJavaのライブラリを呼ぶ with JNI - #define NO_MONEY 0 4.2006/11/08 日記: Java: シンプルな C言語からJava言語を呼び出すJNIサンプル
Java Native Interface 仕様の目次

August 26, 2012

JNI その1:Javaからネイティブコードを呼び出す

Windows上で実施。
コンパイラはボーランドC++コンパイラを使用。
作業するフォルダは C:\work\Chap10\10-2 とする。
下記のフォルダ、ファイルのツリー表示で括弧で囲ってあるファイルはツール(javac、javah、Cコンパイラ)が出力するファイル。

作業フォルダ
│  jni-sample.c
│  [jni-sample.dll]
│  [jni-sample.obj]
│  [jni-sample.tds] … デバッグ用のシンボルファイル
│  [oesf_ace_JniCallToNativeFromJava.h]
│
└─oesf
    └─ace
            [JniCallToNativeFromJava.class]
            JniCallToNativeFromJava.java

  1. Javaのコードを作成
  2. JNIヘッダファイルを作成
  3. ネイティブコードを作成し共有ライブラリの作成
  4. 実行

Javaコードの作成

コードの場所はパッケージに合わせる。以下のコードの場合は.\oesf\ace に置く。

package oesf.ace;
public class JniCallToNativeFromJava {
    static {
        System.loadLibrary("jni-sample");
    }
    public native boolean isOne(int value);

    public static void main(String[] args) {
        JniCallToNativeFromJava jni = 
            new JniCallToNativeFromJava ();
        Boolean res = jni. isOne ( 1 );
        System.out.printf("res = " + res);
    }
}

作業フォルダでjavacコマンドを実行しコードをコンパイルする。

C:\work\Chap10\10-2>javac oesf\ace\JniCallToNativeFromJava.java

JNIヘッダの作成

作業フォルダでjavahコマンドを実行する。引数はファイル名ではなくクラスの完全修飾名。
命名規則に則った名前の関数の宣言がされたoesf_ace_JniCallToNativeFromJava.h というファイルが作業フォルダに作られる。

C:\work\Chap10\10-2>javah -jni oesf.ace.JniCallToNativeFromJava

命名規則は以下。

  • 接頭辞に「Java_」。
  • 完全修飾クラス名を分解し、それらとメソッド名をアンダースコアで連結する。

上記のJavaソースコードから命名規則に従うとネイティブメソッドの名前は

JNIEXPORT jboolean JNICALL Java_oesf_ace_JniCallToNativeFromJava_isOne(JNIEnv *, jobject, jint)

となる。

ネイティブコードを作成し共有ライブラリの作成

書籍に載っていたコードを少し修正。JNIヘッダがCソースファイルと同じフォルダにあるので、JNIヘッダを大なり小なりではなくダブルクウォーテーションで囲むように修正した。

#include <jni.h>
#include "oesf_ace_JniCallToNativeFromJava.h"
jboolean JNICALL 
Java_oesf_ace_JniCallToNativeFromJava_isOne
    (JNIEnv* env, jobject this, jint value) {
    if(value == 1) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

これをコンパイルしてDLLを作成。
インクルードファイルのパスとして
<JDKのフォルダ>\include
<JDKのフォルダ>\include\win32
を追加する。
ボーランドC++コンパイラの場合はコンパイラのbinフォルダにある bcc32.cfg を修正するか、コマンドのオプションに追加する。
今回は bcc32.cfg を次のようにした。

-I"c:\Borland\Bcc55\include";"C:\progra~1\Java\jdk1.6.0_24\include";"C:\progra~1\Java\jdk1.6.0_24\include\win32"
-L"c:\Borland\Bcc55\lib"

DLLは以下のコマンドで作成。

C:\work\Chap10\10-2>bcc32 -c jni-sample.c
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
jni-sample.c:
警告 W8057 jni-sample.c 10: パラメータ 'env' は一度も使用されない(関数 Java_oesf_ace_JniCallToNativeFromJava_isOne )
警告 W8057 jni-sample.c 10: パラメータ 'this' は一度も使用されない(関数 Java_oesf_ace_JniCallToNativeFromJava_isOne )

C:\work\Chap10\10-2>bcc32 -tWD -ejni-sample.dll jni-sample.obj
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland

実行

C:\work\Chap10\10-2>java oesf.ace.JniCallToNativeFromJava
res = true

参考文献:Androidアプリケーション開発標準資格教科書 10-2 Javaからネイティブコードの呼び出し

参考サイト:
Java Native Interface 仕様の目次
Borland C++ Compiler DLL作成
bcc32でDLLを作成する - 偏った言語信者の垂れ流し
Borland C++ Compiler Bcc32コマンドオプション

新潟ミシェウの技ありゴールで勝利、磐田アウェイで3発快勝

J's GOAL:試合詳細:2012 J1 第23節 鹿島アントラーズ 0-1 アルビレックス新潟

勝った。勝ったのはいい。
ミシェウのあのゴールはすごい。「ゆりすぐり」に選ばれたのも納得^^
でも相手は出場停止などで負傷者が多くいたし、ジュニーニョがシュート外しまくってたし、監督も言ってたけど勝ち点3を奪ったんじゃなくてもらった試合だ。
この勝利でなんとか首の皮がつながった感じだけど、この後の残留争いのライバルとの2試合、ホームのセレッソ戦、アウェイのガンバ戦が超重要だ。
セレッソは監督を変えてきたし、ガンバは得点力がすごいことになってる。
この2試合に勝てば勝点が30に到達し残留にかなり近づくが、逆に連敗でもしたら非常にヤバい。
もうここはヤンツーのスカウティング能力でなんとか、なんとか勝利を!!


J's GOAL:試合詳細:2012 J1 第23節 柏レイソル 0-3 ジュビロ磐田

山田復活のゴール!!前田は3試合連続ゴール。
相手の一発退場もあってアウェイで快勝、4位に浮上。
優勝とは言わないまでもACLにはいけるんじゃないか?と期待がまた膨らんできた。

August 24, 2012

JUnit その5:カスタムルール

カスタムルール

org.junit.rules.TestRuleインタフェースを実装することで独自のルールを作成できる。
TestRuleインタフェースは次のメソッドが定義されている。

Statement apply(Statement base, Description description)
戻り値:
新しいStatement。それはbaseと同じもの、baseのラッパー、あるいは完全に新しいStatementかもしれない。

StatementはJUnitのテストを実行するオブジェクト。このクラスには evalute() が定義されている。
evalute() は次のような処理を実行する。

  1. テストクラスのインスタンスの生成
  2. @Beforeの付与されたメソッドの実行(初期化)
  3. テストメソッドの事項
  4. @Afterの付与されたメソッドの実行(後処理)

多くのカスタムルールのapply()はbase(オリジナルのStatement)の前後で独自の処理を実行するStatment、つまりプロキシオブジェクト(ドキュメントで言ってるところのラッパー)を返す。

Descriptionテストケースのメタ情報を保持する。

次のコードはアサーション失敗時のAssertionErrorを拡張するカスタムルール。
JUnitではテスト失敗時にjava.lang.AssertionErrorが送出され、フレームワーク層まで伝達されたエラー情報が表示される。
しかしパラメータ化されたテストではAssertionErrorniテストで使用したパラメータが含まれない。下記カスタムルールにパラメータ情報を追加すれば、テスト失敗のAssertionErrorにパラメータが含まれ、パラメータもエラー情報として表示されるようになる。

package junit.tutorial;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

/**
 * アサーション失敗時の情報を拡張するカスタムルール
 */
public class AssertionMessage implements TestRule {
	
	private StringBuilder msg = new StringBuilder();
	
	public void append(String format, Object... params) {
		msg.append(String.format(format, params));
	}
	
	public String getMessage() {
		return msg.toString();
	}

	@Override
	public Statement apply(final Statement base, Description desc) {
		return new Statement() {
			
			@Override
			public void evaluate() throws Throwable {
				try {
					base.evaluate();
				} catch (AssertionError e) {
					AssertionError e2;
					if (msg.length() > 0) {
						msg.append('\n').append(e.getMessage());
						e2 = new AssertionError(msg.toString());
						e2.setStackTrace(e.getStackTrace());
					} else {
						e2 = e;
					}
					throw e2;
				}
			}
		};
	}
}

次のコードは上記のカスタムルールを適用したCalculatorクラスのテストクラス(乗算のみ)。

package junit.tutorial;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Rule;
import org.junit.experimental.runners.Enclosed;
import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

/**
 * AssertionMessageルールを適用したCalculatorのテストクラス
 */
@RunWith(Enclosed.class)
public class CalculatorRuledTest {

	@RunWith(Theories.class)
	public static class 乗算メソッドのパラメータ化テスト {
		
		@DataPoint public static Fixture DATA1 = new Fixture(3, 4, 12);
		@DataPoint public static Fixture DATA2 = new Fixture(5, 7, 12);
		
		@Rule
		public AssertionMessage message = new AssertionMessage();
		
		@Theory
		public void multiplyで乗算結果が取得できること(Fixture fx) {
			message.append("case: %d * %d = %d", fx.x, fx.y, fx.expected);
			System.out.println(message.getMessage());
			Calculator calc = new Calculator();
			int expected = fx.expected;
			int actual = calc.multiply(fx.x, fx.y);
			assertThat(actual, is(expected));
		}
		
		static class Fixture {
			int x, y, expected;

			Fixture(int x, int y, int expected) {
				this.x = x;
				this.y = y;
				this.expected = expected;
			}
		}
	}	
}

参考記事:WEB DB PRESS Vol.69 JUnit実践入門

参考サイト:
JUnit 4.9の@ClassRuleでクラス単位の前後処理 - 裏紙

August 22, 2012

JUnit その4:ルール

ルール

ルールはJUnit4.7から導入されたもので、共通の初期化処理やテストクラスで横断的に利用する機能をアノテーションを付与した委譲オブジェクトで実現できる。
JUnit4.9からはClassRuleアノテーションが導入された。
JUnit4.10で追加されたRuleChainクラスはルールの実行順序を制御できる。

ルール導入以前は継承を使用してスーパークラスのメソッドやフィールドで共通機能を提供していた。
ルールでは委譲オブジェクトのメソッドやフィールドで共通機能を提供する。継承による共通機能では多重継承ができないが、ルールではいくつでも宣言できる。

ルールの利用方法

ルールを利用するテストクラスにpublicフィールドを宣言する。
#@ClassRuleの場合はpublic static フィールド?
下記のテストクラスでは、JUnitが提供するルールの1つTimeoutを使用している。各テストケースで100m秒以上時間がかかるとそのテストケースは失敗になる。
JUnitが提供するルールには他に ExternalResource, Verifier, ExpectedException などがある。

public class TimeoutExampleTest {
    @Rule
    public Timeout timeout = new Timeout(100);

    @Test
    public void test() throws Exception {
    }
}

Assumeクラスのメソッド

org.junit.Assumeクラスのメソッド assumeThat(), AssumeTrue() は評価がfalseの場合、テストの失敗ではなく無視(ignore)となる。
これは、特定の環境でのテストスキップやパラメータ化テストでのパラメータによるフィルタリングに使用できる。
assumeThat() は assertThat() と同様にMatcherを利用する。assumeTrue() は引数にboolean値を取る。

ExpectedException

@Theory はexpected属性を指定できない。よって構造化したテストクラスでは@Test を付与した場合のような例外のテストはできない。
また、以前の計算機クラスの除算のテストではパラメータによって期待する結果が異なる(割る数が0のときとそうでないとき)ために、コンテキスト(ネストしたクラス)を分ける必要がある。
そのような場合、assumeTrue() と ExpectedExceptionルールを使用するときれいに整理できる。

ExpectedException のインスタンスは ExpectedException#none() で取得する。
検証めっそどには例外が送出されることを検証する expect()、例外メッセージを検証する expectMessage() などがある。

public void expect(Class type)
public void expectMessage(String substring)

下記は計算機クラスのテストクラス。除算メソッドのテストでassumeTrue()、ExpectedExceptionを使用。

package junit.tutorial;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Assume;
import org.junit.Rule;
import org.junit.experimental.runners.Enclosed;
import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;

/**
 * 計算機クラスのパラメータ化テストクラス
 */
@RunWith(Enclosed.class)
public class CalculatorTest {

	@RunWith(Theories.class)
	public static class 乗算メソッドのパラメータ化テスト {
		
		@DataPoint public static Fixture DATA1 = new Fixture(3, 4, 12);
		@DataPoint public static Fixture DATA2 = new Fixture(5, 7, 35);
		
		@Theory
		public void multiplyで乗算結果が取得できること(Fixture fx) {
			String msg = fx.x + "*" + fx.y + "=" + fx.expected;
			System.out.println(msg);
			Calculator calc = new Calculator();
			int expected = fx.expected;
			int actual = calc.multiply(fx.x, fx.y);
			assertThat(msg, actual, is(expected));
		}
		
		static class Fixture {
			int x, y, expected;

			Fixture(int x, int y, int expected) {
				this.x = x;
				this.y = y;
				this.expected = expected;
			}
		}
	}
	
	@RunWith(Theories.class)
	public static class 除算メソッドのパラメータ化テスト {
		
		@DataPoints
		public static Fixture[] DATAS = {
			new Fixture(3, 2, 1.5f),
			new Fixture(10, 2, 5f),
			new Fixture(3, 0, null),
			new Fixture(10, 0, null)
		};
		
		@Rule
		public ExpectedException exception
				= ExpectedException.none();
		
		@Theory
		public void divideで除算結果が取得できること(Fixture fx) {
			Assume.assumeTrue(fx.y != 0);
			String msg = fx.x + "/" + fx.y + "=" + fx.expected;
			Calculator calc = new Calculator();
			float expected = fx.expected;
			float actual = calc.divide(fx.x, fx.y);
			assertThat(msg, actual, is(expected));
		}
		
		@Theory
		public void divideの第2引数に0を指定した場合にはIllegalArgumentExceptionを送出する(Fixture fx) {
			Assume.assumeTrue(fx.y == 0);
			exception.expect(IllegalArgumentException.class);
			Calculator calc = new Calculator();
			calc.divide(fx.x, fx.y);
		}
		
		static class Fixture {
			int x, y;
			Float expected;

			Fixture(int x, int y, Float expected) {
				this.x = x;
				this.y = y;
				this.expected = expected;
			}
		}
	}
}

参考記事:WEB DB PRESS Vol.69 JUnit実践入門

August 19, 2012

FF11やめた

FF11をやめた。正確に書くと「スクウェア・エニックスアカウントを解約した」か。

最近はあまりログインしてなかったし、ログインしてもソロでLV上げ(獣)とか虚ろの調査とかカンパニエopsとか。
MMOやってるのに意味ないじゃんw
ログインしなくても禁断症状とかでないし、やめてもいいかなと。

キャラクターの削除をしなかったので復帰の可能性は0ではない。明日から仕事なので、その仕事がどれくらい大変かしばらく様子を見る。その結果、復帰するということももしかしたらあるかもしれない。

たた、まずFF11するより睡眠時間を確保したいし、FF11するならアニメ見るか、映画見るか、他のゲームやりたい。積んであるいくつかのゲーム(SIREN NEW TRANSLATION、アーマードコアなど)、それにこれから出るサモンナイト5とか。
まあ、復帰の可能性はかなり低い。

不安なのはFF11で繋がってた大学の同期にして以前勤めていた会社の同期でもある人と疎遠になることくらいかなー。
まあお互い電話番号やメールアドレスはわかるし、たまにメールするようにしておけば大丈夫かな。

FF11、今まで楽しませてくれてありがとう。
これからも細く長くサービスが続くといいね。

明日から仕事

明日から仕事だ。2年半以上に渡って予備労働者wだったが、労働者に復帰。

久しぶりなので仕事することはもちろん不安だが、それ以上に電車通勤が不安w
遠くはないが乗り換えがあるのが嫌だ。
がんばれば自転車で行ける距離。秋になって涼しくなったら自転車で行こうかな。
まあ当分は電車通勤だ。

今度はどれくらい保つかなw

JUnit その3:テストクラスの構造化とパラメータ化テスト

テストクラスの肥大化への対応としてテストクラスの構造化とパラメータ化テストがある。

テストクラスの構造化

org.junit.experimental.runners.Enclosedクラスはネストしたクラスをテストとして扱うことができるテストランナー。
テストをコンテキスト(初期状態、内部状態など)で分割し、それぞれのテストクラスをネストしたクラスにすればテストクラスを構造化できる。

パラメータ化テスト

パラメータ化テストに関数するクラス・アノテーションは org.junit.experimental.theories パッケージに定義されている。 Theoriesクラスはパラメータ化されたユニットテストをサポートするテストランナー。
テストメソッドには @Test ではなく @Theoryを付ける。これでテストメソッドに引数を取らせることができる。
パラメータ(テストデータ)は @DataPoint または @DataPoints を使用する。@DataPoint ではテストデータをpublic staticなフィールドまたはメソッドで1つずつ定義する。たとえば

@DataPoint
public static int INT_PARAM_1 = 3;
@DataPoint
public static int INT_PARAM_2 = 4;

とパラメータを定義した場合、

テストメソッド(3, 3)
テストメソッド(3, 4)
テストメソッド(4, 3)
テストメソッド(4, 4)

のようにパラメータの順列の数だけすべてのパターンでテストが行われる。各パターンごとにインスタンスが生成されるので、初期かも各パターンごとに行われる。

@DataPointsではテストデータを配列としてまとめて定義する。
サンプルコードでは割られる数、割る数、期待値の3つのパラメータが必要だがすべての順列のパターンでテストが行われるのは問題なので、引数をオブジェクトにしてオブジェクトの配列をパラメータとして定義している。

@RunWith(Enclosed.class)
public class CalculatorParameterizedTest {

	@RunWith(Theories.class)
	public static class 乗算メソッドのパラメータ化テスト {
		
		@DataPoint public static Fixture DATA1 = new Fixture(3, 4, 12);
		@DataPoint public static Fixture DATA2 = new Fixture(5, 7, 35);
		
		@Theory
		public void multiplyで乗算結果が取得できること(Fixture fx) {
			
			String msg = fx.x + "*" + fx.y + "=" + fx.expected;
			System.out.println(msg);
			Calculator calc = new Calculator();
			int expected = fx.expected;
			int actual = calc.multiply(fx.x, fx.y);
			assertThat(msg, actual, is(expected));
		}
		
		static class Fixture {
			int x, y, expected;

			Fixture(int x, int y, int expected) {
				this.x = x;
				this.y = y;
				this.expected = expected;
			}
		}
	}
}

参考記事:WEB DB PRESS Vol.69 JUnit実践入門

August 18, 2012

JUnit その2:aseertThat()とMatcher API

アサーション

アサーションとは比較検証の仕組みの事。
JUnit4.4以降では assertThat() と Matcher API を使用する。
以下のような特徴がある。

  • より自然言語(英語)に近い記述
  • 比較方法(Matcher)と検証フレームワーク(asertThat())の分離
  • より詳細なエラー情報

Matcher

Matcher は自然言語に近い記述をするためにstaticインポートして使う。
Matcher は org.hamcrest.CoreMacther#is() のようなstaticなファクトリメソッドを介して使用する。is()以外に次のようなさまざまなファクトリメソッドがある。

  • CoreMatcher#nulValue()
    実測値がnullであるかを評価する。
  • CoreMatcher#not()
    他の Matcher の評価値を反転する。
  • org.junit.matchers.JUnitMatcher#hasItem()
    リストや配列などIterableインタフェースを実装したクラスのインスタンスの実測値に期待する値が含まれているかを評価する。

カスタムMatcher

独自のMatcherを作ることができる。org.hamcrest.BaseMatcher を継承して作成するとよい。
BaseMatcher は org.hamcrest.Matcherインタフェースを実装しており、カスタムMatcherはファクトリメソッドとMatcherインタフェースのmatches()、describeTo() を実装する。

boolean matches(Object item)
評価するメソッド
void describeTo(Description description)
評価の失敗時に理由を通知するためのメソッド

DescriptionクラスのappendValue()で追加された値はダブルクウォーテーションで囲まれて表示される。appendText()で追加された値はダブルクウォーテーションで囲まれずそのまま表示される。

下記はDateの日付(年/月/日)だけを比較するカスタムMatcher。

package junit.tutorial;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;

/**
 * カスタムMatcher。Dateの日付(年/月/日)だけを比較する
 */
public class IsDate extends BaseMatcher<Date> {

	private final int year;
	private final int month;
	private final int date;
	
	Object actual;
	
	IsDate(int year, int month, int date) {
		this.year = year;
		this.month = month;
		this.date = date;
	}
	
	@Override
	public boolean matches(Object actual) {
		
		this.actual = actual;
		if (!(actual instanceof Date)) {
			return false;
		}
		
		Calendar cal = Calendar.getInstance();
		cal.setTime((Date)actual);
		if (year != cal.get(Calendar.YEAR)) {
			return false;
		}
		if (month != cal.get(Calendar.MONTH) + 1) {
			return false;
		}
		if (date != cal.get(Calendar.DATE)) {
			return false;
		}
		return true;
	}

	@Override
	public void describeTo(Description desc) {
		desc.appendValue(year + "/" + month + "/" + date);
		if (actual != null) {
			desc.appendText(" but actual is ");
			desc.appendValue(new SimpleDateFormat("yyyy/M/d").format((Date)actual));
		}
	}

	public static Matcher<Date> dateOf(int year, int month, int date) {
		return new IsDate(year, month, date);
	}
}

参考記事:WEB DB PRESS Vol.69 JUnit実践入門

August 16, 2012

JUnit その1

JUnit3とJUnit4

現時点での最新版は2011年10月リリースの4.10。
JUnit3もまだかなり使われているようだ。「Androidアプリケーション開発標準資格教科書」にはAndroidのテストフレームワークはJUnit3をベースにしていると書いてある。
JUnit3とJUnit4の相違点はかなり大きい。以下はJUnit4を前提とした内容。

Quick JUnit

Quick JUnitはEclipseのプラグイン。
Ctrl+9 でテスト対象クラスとテストクラスの切り替え、Ctrl+0 でテストの実行ができる。
Eclipseマーケットプレイスからインストールできる。

プロジェクトの準備

JUnitの最新版を使いたい場合はプロジェクトのフォルダにJarファイルを追加し、そのJarファイルのコンテキストメニューで[ビルド・パス]-[ビルド・パスに追加]とする。 Eclipseに付属のJUnitを使う場合は、テストクラスやテストスイートをウィザードで作成するときに自動的にビルドパスに追加される、と思う。
自分が使っているEclipse Indigoには3.8.2、4.8.1、4.8.2があるようだ。

テストコード用のソースフォルダを作る。プロジェクトのコンテキストメニューの[ビルド・パス]-[新規ソースフォルダー、または[新規]-[その他]の[ソースフォルダー]でソースフォルダtestを作成する。
テストクラスはテスト対象と同じパッケージにするあが、置く場所は異なるフォルダにするのがいいらしい。

テストクラスの作成

テスト対象クラスのコンテキストメニューで[新規]-[その他]から[JUnitテスト・ケース]または[JUnitテスト・スイート]を選択してウィザードを使用してテストクラスを作成できる。
また、Quick JUnitではインストール済みの場合はテスト対象クラスをエディタで開きCtrl+9でウィザードが起動する(テストクラスがある場合はテストクラスに切り替わる)。

JUnitのルール

JUnitのテストコードには以下のルールがある。

  • テストクラスはpublicクラス。
  • テストメソッドはorg.junit.Testアノテーションを付与したpublicメソッド。
  • テストメソッドは戻り値がvoidで引数を持たない。
  • パラメータ化テストメソッドはorg.junit.experimental.theories.Theorアノテーションを付与したpublicメソッド
  • パラメータ化テストメソッドは戻り値がvoidで引数を取る。

実測値と期待値の比較にはスタティックメソッド org.junit.Assert#assertThat() を使用する。

public static <T> void assertThat(T actual, org.hamcrest.Matcher<T> matcher)
public static <T> void assertThat(String reason, T actual, org.hamcrest.Matcher<T> matcher)
引数
  reason - エラーについての追加情報
  actual - 実測値(actual value)
  matcher - 期待値との比較を行うMatcherオブジェクト

Matcherオブジェクトはファクトリメソッドで作成する。サンプルコードではis()がファクトリメソッド。

例外が発生することを期待するテストではTestアノテーションのexpected属性に発生する例外クラスのclassをセットする。

テストの実行

テストクラスのコンテキストメニューで[実行]-[JUnitテスト]を選択して実行する。
Quick JUnitではテストメソッド内にカーソルを置いてCtrl+0で実行する。

その他、気付いたこと

JUnitではアノテーションを利用しているが、アノテーションは型なのでimportしないと使えない。
#標準アノテーション(Overrideとか)はimportしなくてもいいみたいだが。java.langパッケージにあるからか?
Version#id()はJUnitのバージョンを返す。

System.out.println("junit version: " + junit.runner.Version.id());

下記は計算機クラス(掛け算・割り算)とそのテストクラス。
参考記事に従ってテストメソッド名を日本語にしてある。わかりやすいが違和感を覚える人もいるかも。

テスト対象クラス
package junit.tutorial;

/**
 * 計算機クラス
 */
public class Calculator {

	public int multiply(int x, int y) {
		return x * y;
	}

	public float divide(int x, int y) {
		if (y == 0) {
			throw new IllegalArgumentException();
		}
		return (float)x /(float)y;
	}
}
テストクラス
package junit.tutorial;

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import static junit.tutorial.IsDate.*;

import java.util.Date;

import org.junit.Test;

/**
 * 計算機クラスのテストクラス
 */
public class CalculatorTest {

	@Test
	public void multiplyで3と4の乗算結果が取得できる() {
		Calculator calc = new Calculator();
		int expected = 12;
		int actual = calc.multiply(3, 4);
		assertThat(actual, is(expected));
	}

	@Test
	public void multiplyで5と7の乗算結果が取得できる() {
		Calculator calc = new Calculator();
		int expected = 35;	// 35が正しい期待値
		int actual = calc.multiply(5, 7);
		assertThat(actual, is(expected));
	}

	@Test
	public void divideで3と2の除算結果が取得できる() {
		Calculator calc = new Calculator();
		float expected = 1.5f;
		float actual = calc.divide(3, 2);
		assertThat(actual, is(expected));
	}

	@Test(expected = IllegalArgumentException.class)
	public void divideの第2引数に0を指定した場合にはIllegalArgumentExceptionを送出する() {
		Calculator calc = new Calculator();
		calc.divide(5, 0);
	}
	
	@Test
	public void 第3章のカスタムMatcherの確認() {
		assertThat(new Date(), is(dateOf(2012, 1, 12)));
	}
}

参考記事:WEB DB PRESS Vol.69 JUnit実践入門

参考サイト:
JUnit4 - TRANCE ARTS 技術情報Wiki
「テスト駆動開発入門」を JUnit 4.4 と Eclipse 3.3 でやってみる (1)
JUnit4をやってみよう
JUnitメモ(Hishidama's JUnit Memo)

August 15, 2012

帰省してキス釣り2012

この前の週末、帰省して海釣りしてきた。
2日間ともキスがターゲット。エサはアオイソメ。

初日、8/11(土)。満潮08:40。干潮16:49。天気は少し陽も差したが大体曇りだった。
投げ竿を持って出かけた。5時半前に開始。近くの川の河口近くの砂浜をスタートし、しばらく釣ったら川から離れるように移動するのを繰り返した。
いきなり一投目で2匹、2投目で1匹釣れた。今日はすごい釣れそうだと思ったがそこから全く当たりがなくなり、4匹目が釣れたのが8時頃orz
結局10時半くらいまで、約5時間でキス7匹。あとはヒイラギ2匹と何かの稚魚が1匹。ヒイラギは捨てて稚魚は逃がした。
1匹だけ15cm超の大きめのキスが釣れたのでうれしかった。
周りで釣っている人たちはもっと釣れていた。やはり遠投できないとだめなのかなー。

2日目、8/12(日)。満潮09:44。干潮17:59。天気は曇りから後半は雨だった。
漁港の防波堤からシーバスロットでちょい投げ。昨日より1時間早い4時半から開始。
最初の防波堤の根本付近で港の内側に投げたらいきなり根がかりして錘と仕掛けをロストorz
内側は根がかりが怖いのでその後は港の外側のテトラ上でちょい投げしてキスをねらった。
2投目で1匹釣れたが、それから全くだめ。餌捕りもない。
2本針の仕掛けを使っていたが途中で針が1つはずれてかなり時間1本針で釣っていたw
9時頃に雨が降り始めたが、そこから当たりが出始めて。今まで全く反応がなかったのだが、5投連続で反応有り。
5連続の1投目は釣れていたが、上げるときにテトラに当たって仕掛け毎ロストorz
キス用仕掛けがなくなったので、ハゼ釣りの仕掛けを付けた。それでも当たりが来た。
2投目は釣り上げたが、クーラーボックスに持っていく途中に針からはずれて海にポチャンorz
3投目は釣り上げた。やっと今日2匹目^^;
4投目はタイの稚魚っぽいのだったのでリリース。
5投目は餌だけ取られた。
とにかく9時過ぎから急に釣れそうな感じに。しかし雨も強くなりテトラが濡れて滑りやすくなり危険だったので泣く泣く竿を納めた。
港の方に戻る途中に外側で釣っている人を見てみるとやはりみんな釣れていた。あーもったいねーなー;;


以下、思ったことメモ。
・キャスティングでは10g単位でアオイソメを売っている。今回50gだったが40gでも自分の釣り方なら足りる。
・仕掛けは十分に持っていく。最低4セット。
・今まで2本針、3本針の仕掛けを使っていたが、5本針の仕掛けを今度使ってみよう。

就職活動を終えて

3年弱のニート生活を今週で終える。
残念ながら望んだような仕事には就けなかった。年齢とブランクを考えたら当然か。
選り好みしてられる状況ではなくなったので、とりあえず内定が出たところで働くことになった。


応募のほとんど(9割くらい)は書類選考で落ちたが、面接まで行ったのもいくつかあった。
面接・筆記試験で気が付いたことをメモっておく。


・当たり前だが志望動機を聞かれる。一番困る。どうしてこの業界か。どうしてうちの会社か。
・これまでの仕事でうまくいったもの・うまくいかなかったものを聞かれた。さらにどちらについても技術的なツッコミを入れられて答えに窮した。過去の仕事について整理しておいた方がいい。
・筆記試験で数学の確率・場合の数・順列・組み合わせの問題が出来なかった。少し勉強しておくべきだった。

August 09, 2012

京急将棋まつり

行ってきました。瀬川さんを見にw
場所は京急百貨店の上大岡店の催事場。

午後の勝又教授の講座のところから参加した。
教授も急遽聞き手を務めることになった(将棋カルタの宣伝をしに来た?)高橋和女流三段も話が上手くて聞きやすかった。前の方にいるうるさい子供のあしらい方も、講演が多い教授と子供将棋教室をやっている高橋女流だからさすが手慣れているw

Keikyu_shogi_20120809_2

1時半から勝又教授とせがーさんの局面指定対局。解説は森内名人。
写真は対局前にひふみん以外の棋士・女流棋士が並んだので撮った。左端は総合司会の鈴木環那女流二段、右端はこの対局の聞き手の熊倉紫野女流初段。瀬川さんが一番背が高い、というか、ガタイがいいという感じがした。水泳で体を鍛えてるからか?
対局はちょっと見て、そこまで開場を後にした。解説聞いてもおいらはわからないし、ずっと立ってるのがきついからw
それにサイン会は名人、ひふみん、女流棋士で勝又教授とせがーさんのはなかったので。

平日なのでそれほど混んでないかと思ったがそこそこ人がいた。
羽生二冠が来る明日と週末の3日間は相当混むだろうなあ。
しかし土曜日の「佐藤さん大集合」はウケるw
#佐藤康光王将、佐藤義則八段、佐藤秀司七段、佐藤天彦7段、佐藤紳哉六段、佐藤和俊五段、佐藤慎一四段ww

はじめて生でプロ棋士を見れたので良かったが、運営について一言。
講演や対局が行われる方の座席の席取りがひどい。ハンカチやらバッグやらを置いて席取りをしているのだが、講演・対局が始まっても戻ってこない人が多数いる。席が空いているのに座れなくて後ろで立ち見している人が多数。これは来年以降なんとかして欲しいと思う。

August 07, 2012

ぱいかじ南海作戦

先週の水曜日、「毎月1日は映画が1000円」というのを知って、「ぱいかじ南海作戦」を見てきた。
なぜこの映画かというと、出演が阿部サダヲ、佐々木希、貫地谷しほりと結構好きな役者さんなことと、沖縄の離島が舞台だから。
正直、原作者の椎名誠はあまり好きではないのだが、それでも上記の見たい理由の方が大きかったので行ってきた。

撮影場所が西表島なら面白いなと思っいてたが、ずばり西表島だったw
主人公が船でたどり着いたところはどうみても上原港。レンタカーはやまねこレンタカー。
これだけでも自分には見る価値があったw
まあ話は正直、しょうもない。
でも阿部サダヲは面白いし、佐々木希と貫地谷しほりはかわいい。
1000円なら見て損したとは思わなかった。
#1800円だったらビミョーw


ちなみになぜ椎名誠が好きではないかというと、相当昔の話だが椎名誠は「白い馬」という自分の作品を自分で監督して映画化した。で、その作品を見た某TV局のアナウンサーが番組内で面白くなかったと言っちゃった。それに対して椎名誠がTV局に抗議したということがあったのだ。
その話を聞いて「モンゴルの広い草原を舞台にした話を書いた人なのに批評に抗議するなんて心が狭いなー」と感じて、それ以来、椎名誠があんまり好きではないのだ。


ぱいかじ南海作戦」公式サイト

« July 2012 | Main | September 2012 »

March 2017
Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof