ひ孫

犬のこととか書いていきたい

Junit4でテストの順序を制御する

例えばinsertTestをしたあとにdeleteTestをするようなことをしたいなーというようなとき、JUnit3ではTestSuiteで行えた*1

junit3なら

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class TestOrderForJunit3 extends TestCase {

    public static Test suite() {
        TestSuite suite = new TestSuite();

        suite.addTest(new TestOrderForJunit3().new DbTest("testInsert"));
        suite.addTest(new TestOrderForJunit3().new DbTest("testDelete"));

        return suite;
    }

    public class DbTest extends TestCase {

        public DbTest(String name) {
            setName(name);
        }

        public void testInsert() {
            System.out.print("insert");
        }

        public void testDelete() {
            System.out.print("delete");
        }
    }
}

こんな感じ。
しかしこれをJUnit4のTestSuiteに置き換えようとしたとき、クラス単位で順序制御をするようなやり方はあったが
メソッド単位での制御を制御するやり方は見つからなかった。
なので最初に考えたのは1クラス1テストのクラスを作るやり方。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({
    TestOrderForJunit4.InsertTest.class,
    TestOrderForJunit4.DeleteTest.class,})
public class TestOrderForJunit4 {

    public static class DeleteTest {

        @Test
        public void testDelete() {
            System.out.println("delete");
        }
    }

    public static class InsertTest {

        @Test
        public void test2() {
            System.out.println("insert");
        }
    }
}

こんな感じ。
内部的にクラスを持たせてそれをSuiteClassesで呼び出すようなことをしたら出来た。
どうも内部のclassはstaticでないといけない模様。
なんだかスマートじゃないのでもうちょっと調べてみたらStackoverflowにそのものずばりなやり方があった

http://stackoverflow.com/questions/3089151/specifying-an-order-to-junit-4-tests-at-the-method-level-not-class-level

上のよりも下の@Orderのアノテーションを自作するのがとても良さ気
以下コード抜粋。

まずアノテーションになるクラスを作る
Order.java

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
    public int order();
}

*2

で、それの順序制御をするクラス
OrderedRunner.java

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class OrderedRunner extends BlockJUnit4ClassRunner {
    public OrderedRunner(Class<?> klass) throws InitializationError  {
        super(klass);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        Collections.sort(list, new Comparator<FrameworkMethod>() {
            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);
                
                if (o1 == null || o2 == null){
                    return -1;   
                }
                return o1.order() - o2.order();
            }
        });
        return list;
    }
}

*3

とこんなふうに準備をして

@RunWith(OrderedRunner.class)
public class TestUseOdered {
    
    @Test
    @Order(order=2)
    public void deleteTest(){
        System.out.println("delete");
    }
    
    @Test
    @Order(order=1)
    public void insertTest(){
        System.out.println("insert");
    }
}

とすると@Orderで指定した順序が若い方から実行される。素敵!

*1:そもそも順序を気にするようなテスト書くなという話はあるけども。今回はまあひとつ方法をやってみるという話

*2:アノテーションってこんなふうに自作するんですね

*3:InitializationErrorはなんか2つぐらいクラスがあるみたいなので最初ハマった。

Wordpressで投稿者を非表示にしたい

例えば複数人で管理していてそれぞれ別アカウントで管理してるけどそれぞれの名前がRSSに出ちゃう!
とても困る!
みたいな事があった。
しかしRSSFeedもatomやらrss2やら色々あって全部書き換えるのは大変!

というような状況に陥ったので、いっそ元凶のthe_authorを潰すようなコードで対処した

wp-incluide/author-template.php

    function the_author( $deprecated = '', $deprecated_echo = true ) {
        return ""; // 追加行。
	if ( !empty( $deprecated ) )
		_deprecated_argument( __FUNCTION__, '2.1' );
	if ( $deprecated_echo !== true )
		_deprecated_argument( __FUNCTION__, '1.5', __('Use <code>get_the_author()</code> instead if you do not want the value echoed.') );
	if ( $deprecated_echo )
		echo get_the_author();
	return get_the_author();
    }

荒々しい。なんかいいプラグインとか無いんだろうか。

MRUnitいろいろ

Hadoopのテストケースを書くとき、MapやReduceのテストというのは結構書きづらかった。
ちょっと調べたら、MRUnitというのがあるので使ってみた。

基本的なセットアップなんかや利用サンプルはぐぐればたくさん出てくるので
自分で調べても出なかったようなこととかまとめてみる。

addOutputとwithOutputの違い

これ何が違うんだろうと思ったけど基本的に動作は一緒らしい。
違いはaddOutputはvoidを返すのに対してwithOutputは自身を返す。
つまりwithOutputだとメソッドチェーン的な感じに書けるっぽい
コードで書くと

// addOutputで書く場合
reduceDriver.addOutput(new Text("hello"), new Text("1"))
reduceDriver.addOutput(new Text("world"), new Text("1"))
reduceDriver.addOutput(new Text("candy"), new Text("2"));
// withOutputで書く場合
reduceDriver.withOutput(new Text("hello"), new Text("1"))
            .withOutput(new Text("world"), new Text("1"))
            .withOutput(new Text("candy"), new Text("2"));

みたいな。withInput,addInputも同様

runとrunTest

runTest()は名前の通り上記のwithOutputやaddOutputをして、入っているものと比べて違ったら失敗としてエラーを返す。
基本的にはこれを使うことになる。*1
もちろんこれが普通なのはいいんだけ「この一部の結果さえあってりゃいいんだよなー」みたいなこともある。*2 *3

そんな時はrun()がよい。
これはwithOutputのチェックをせずにただ単純に結果が返ってくる。
例えばhelloとworldの結果はチェックしたいけどそれ以外はとりあえずどうなっててもいいというテストを書きたかったら

       List<Pair<Text, Text>> expectPair = new ArrayList<Pair<Text,Text>>();
       expectPair.add(new Pair(new Text("hello"), new Text("1"));
       expectPair.add(new Pair(new Text("world"), new Text("1"));
       List<Pair<Text, Text>> resultPair = reduceDriver.run();
       for (Pair<Text, Text> expectPair : expect) {
            boolean hasEqualPair = false;
            for (Pair<Text, Text> resultPair : result) {
                if(expectPair.equals(resultPair)){
                    hasEqualPair = true;
                }
            }
            //一致するものがなかったら失敗にする
            if(hasEqualPair == false){
                fail();
            }
        }

みたいな。

*1:ちなみにrunTestの第一引数がorderMatterとなっているのでrunTest(false)として実行すれば順序は無視されるっぽい

*2:あんまりないかも。

*3:自分の場合は余分な合算値の情報なんかをだらだら出していることとかがあったのでその場合にちょっとだるいこととかがあった

Linux mint 13(maya)のmate terminalで右クリックすると新しいウィンドウが開きまくる

vmで動かしていたubuntuがkernel panicでおなくなりになったので
現在絶好調らしいmintを試してみた。

最初に気になったのが端末画面で右クリックするとなぜか新しい端末が開くこと。

なんだこれはと思ってよくよく見ると右クリックしたコンテキストメニューの一番上が誤爆するらしい。
画面右端だとこれは起きなかった。

いったいどういうことだと悩んでいたらいつの間にか治っていた。
そのとき upgradeをかけていたのでもしかしてと別のマシンで

sudo apt-get upgrade mate-terminal

と試してみたらなおった。
焦るなって話ではあるけどこれ一歩間違えてたらmintは使わない判断していたかもなあ・・・

あとmintさん.bashrcがデフォルトで存在してないのが気になる。
あんまりいろんなディストリビューション触ったこと無いけどデフォルトで存在してないのって割りと普通だったりするんだろうか?

GoogleAnalyticsに複数トラッキングコードを埋め込む

Google analyticsの日本語ヘルプを見ると
https://support.google.com/analytics/bin/answer.py?hl=ja&answer=1032400

というがっかりなことが書いているけどぐぐってみると結構出てくる。
が、英語版を調べると
こんなページが
https://developers.google.com/analytics/devguides/collection/gajs/?hl=ja#MultipleCommands

できるじゃないですか!日本語版嘘じゃないですか!

上記に乗っているけどやり方としては

gaq.push(
  ['fuga._setAccount', 'UA-XXXXX-1'],
  ['fuga._trackPageview'],
  ['hoge._setAccount', 'UA-XXXXX-2'],
  ['hoge._trackPageview']
);

という感じで名前空間的に使うと良いみたい。

Redmineでsvnのリポジトリブラウザで空白を無視して差分表示する

redmineで差分が無視されないのが気に入らなかったのでなんとかならないかと調べた。

そしてたどり着いたのが下記。
http://www.redmine.org/issues/1476

しかしどうも中身のソースをいじるのはちょっと微妙。
そこで調べると、subversion 1.7なら設定で解決できそうな気がした
(今回はredmineの実行ユーザーの設定。グローバルな設定とかがあるのかはしらない)

~/.subversion/config

[helpers]
diff-cmd = diff 
diff-extensions = -u -w -p -b

diff-extensionsはdiffを行う歳に使われるオプションみたいな感じで実際には

svn diff -x -u -w -p -b

みたいなコマンドが投げられるらしい。
diff-extensionsは1.7からの機能らしい。

もし諸般の事情で単純にアップデートできない環境だったりしたら1.7をどこかに別途インストールして
redmineのconfig/configuration.ymlで使うsvnを指定してやるといいかもしれない。

Hadoopで拡張子を.tsvで吐き出す

HadoopでTextOutputFormatを使うと気軽に吐き出してくれて便利なんだけど拡張子がつかない状態で出力される
で、いちいち拡張子書きなおしてやるのが面倒になったのでTsvで出力するFormatを作った

https://gist.github.com/3367138

public class TsvOutputFormat<K,V> extends TextOutputFormat<K, V> {
    @Override
    public RecordWriter<K, V> getRecordWriter(TaskAttemptContext job) throws IOException, InterruptedException {
        Configuration conf = job.getConfiguration();
        boolean isCompressed = getCompressOutput(job);
        String keyValueSeparator= conf.get("mapred.textoutputformat.separator",
                                        "\t");
        CompressionCodec codec = null;
        String extension = ".tsv"; //変更箇所。
        if (isCompressed) {
        Class<? extends CompressionCodec> codecClass = 
            getOutputCompressorClass(job, GzipCodec.class);
        codec = (CompressionCodec) ReflectionUtils.newInstance(codecClass, conf);
        extension = codec.getDefaultExtension();
        }
        Path file = getDefaultWorkFile(job, extension);
        FileSystem fs = file.getFileSystem(conf);
        if (!isCompressed) {
        FSDataOutputStream fileOut = fs.create(file, false);
        return new LineRecordWriter<K, V>(fileOut, keyValueSeparator);
        } else {
        FSDataOutputStream fileOut = fs.create(file, false);
        return new LineRecordWriter<K, V>(new DataOutputStream
                                            (codec.createOutputStream(fileOut)),
                                            keyValueSeparator);
        }
    }
}

内容としてはTextOutpuFormatを継承してgetRecordWriteをoverrideしてコードを丸コピして

String extension = ""; 

となっているのを

String extension = ".tsv"; 

とすることで実現している。

・・・はい。コピペコード。良くないですね。上記はhadoop1.03のものを使っています。多分バージョンが変わったら使えない・・・
このextensionをメンバ変数かprotectedメソッドあたりから返すようにしてくれていればこんなことしなくていいんですけどね。