Mac OS X で maven の出力が文字化けする

JUnit4 の Parametrized を Groovy で使う

概略

現在こっそり開発しているツールのテストに Groovy を使用しています. テスト対象は Java なのですが, テストには柔軟に書ける Groovy が便利です.

プロジェクト自体は Maven3 で管理していて, ビルド, テスト, パッケージングを全て pom.xml で設定しています. そこのちょっとした設定の間違いで数時間ハマってしまったので, 記録として残しておきます.

Groovy テストスクリプト

JUnit4 ではパラメータ化されたテストを実行することができます.

クラスに org.junit.runner.RunWith アノテーション, パラメータを供給するメソッドに org.junit.runners.Parameterized.Parameters アノテーションを付けると, そのクラスはパラメータ化されたテストを実行できるようになります.

詳しいことは http://groovy.codehaus.org/Using+JUnit+4+with+Groovy あたりを見てください.

pom.xml

pom をそのまま書くのが面倒なので,

<aaa>
  <bbb>
    <ccc>value</ccc>
  </bbb>
</aaa>

というのを

aaa
`-bbb
    `-ccc=value

と書くことにします.

プロジェクトの build 要素以下はこのようになっています.

build
`-plugins
  `-plugin
    `-groupId=org.codehaus.gmaven
    `-artifactId=gmaven-plugin
    `-version=1.4
    `-configuration
    | `-providerSelection=1.8
    `-executions
    | `-execution
    |   `-goals
    |     `-goal=generateStubs
    |     `-goal=compile
    |     `-goal=generateTestStubs
    |     `-goal=testCompile
    `-dependencies
      `-dependency
        `-groupId=org.codehaus.groovy
        `-artifactId=groovy-all
        `-version=1.8

この pom で mvn clean test を実行すると以下のようなエラーが出ました.

シンボルを見つけられません。
シンボル: クラス Parameterized$Parameters
場所    : org.junit.runners の パッケージ

Groovy で書いたテストスクリプトが Java クラスに変換され, さらに javac でコンパイルされます. その段階で上のエラーが出ています.

確かに Groovy から変換された Java ソースを見ると @Parameter アノテーションが @org.junit.runners.Parametrized$Parameter に変換されてしまっています. Java ソースとしては $ ではなく . が正しいはずなのでコンパイルエラーが出た理由が分かりました.

しかし, いくらググってもこの Groovy スクリプトのマズいところが見付かりません.

ふと思い立って goal=generateTestStubs の部分を消してみました. 「変な Java ソースを生成するくらいなら, そこを消してしまえ」と, 思い立ったというよりヤケクソでというのが正しい表現な気もします. すると無事テストが実行されました.

なぜかと考えてみると, そもそも generateTestStubs で生成された Java ソースは, メソッドの本体が return null のような意味の無いクラスでまさにスタブでした. なぜこのようなクラスが必要になるのかと言うと, おそらく Java 側からこの Groovy スクリプトを呼び出すコードをコンパイルするためのクラスなのでしょう. 今回, そのような Java クラスは無いので実はこのゴールは不要なのでした.

まとめ

ということで答えは「余計なゴールがあったために, 変な Java ソースをコンパイルしなくてはならなくなり, そこでエラーが起きていた」ということでした.

う〜ん, 我ながら嫌なハマり方をしましたが, 解決できたときはスッキリしました.

そして教訓は「ツールについてきちんと理解しよう」ということでした. maven は便利なツールですが, まだ自分にとって複雑なツールです. まだまだ勉強しないとですね.

それでは.

Python で書く代数

代数は数学の中でもプログラミング言語と近しい存在です. 歴史的な事実関係は逆でプログラミング言語が抽象代数的な発想の中から生まれてきた, と言えるでしょう.

この記事では代数学上の事柄について, プログラミング言語の Python を使って記述していきます.

代数

現代の抽象的な代数は公理による定義を行います. 「公理」というのはひらたく言うとルールのことで, そのルールから証明という手続きを経て, 命題や定理を導き出します.

公理では

  • あるべき対象
  • あるべき演算
  • 満たすべき性質

について記述します.

プログラミングで言えば

  • あるべきオブジェクトや持っているべきプロパティ
  • あるべきメソッドやオペレータ
  • オブジェクトやメソッドが持つべき性質

にあたり, これらはテストでチェックしたり, Java の interface および abstract class 相当の仕組みを利用して, 実装しなければならないメソッドを表現します.

この対応関係を着想として, まずは代数的対象のうち「群 (group)」を Python で表現してみます.

ここで載せるソースコードは全て https://bitbucket.org/cocoatomo/pyarith にあります. 順次アップデートしていきます.

群を扱った Python スクリプトは https://bitbucket.org/cocoatomo/pyarith/src/1b3a578e1678/group.py です. テストスクリプトは https://bitbucket.org/cocoatomo/pyarith/src/1b3a578e1678/tests/grouptest.py です.

クラス Group を継承したクラスが群となり, そのクラスのインスタンスが群の元に相当します.

abstract method として実装してある __eq__, __add__, __neg__ メソッドが, 群であるために必須のメソッドとなります. a = b は実は a.__eq__(b) のことであり, a + b, -a もそれぞれ a.__add__(b), a.__neg__() に相当します. またテストの方でその存在をチェックしている ZERO というフィールドも, それが存在することが群に課せられた条件です. つまり, 群には「等号比較」「加法」「零元」「逆元」が必須の条件なのです. (等号の性質に関しては通常は群の公理に含めません. ここでは等号も実装しているので, 等号の性質も一緒にテストしています.)

これらは好き勝手に実装して良いのではなく, 満たすべき制約条件があります. このテストスクリプトでは, それをテスト関数の形で表現しています. (演算によって群が閉じていることのテストが抜けていますね…… 追加しておきます.)

テストケースとその意味
test_equals_reflexive_law 等号の反射律
test_equals_symmetric_law 等号の対称律
test_equals_transitive_law 等号の推移律
test_add_ZERO 零元の性質
test_add_negative 逆元の性質
test_add_commutative_law 加法の可換性
test_add_associative_law 加法の結合律

それぞれの制約がどういうものかはスクリプトを見ればすぐに分かると思います. 群の公理 (ルール) はこれで全てです. http://ja.wikipedia.org/wiki/%E7%BE%A4_(%E6%95%B0%E5%AD%A6) と見比べて確認してみてください.

(可換性は全ての群が備えているものとは限りません. 可換性を持つ群を「可換群」, 可換性を持たない群を「非可換群」と呼びます.)

まとめ

この記事では, 群の公理を abstract method とテスト関数を使って記述しました. Python スクリプトとして記述することで少しだけ理解しやすくなれば嬉しく思います.

これだけの少ないルールから群の様々な性質が証明されていくのは不思議ですね.

次回は環や体を対象としたり, テストを行うのに使用した pytest モジュールについて書こうと思います.

それでは.

Licenses