世界初のscala-native用テストライブラリ! といっても過言ではない。*1
リリースしたのは先週ですが、実装にばかり時間をかけていたら、解説というか経緯などを書いてる暇がなかったので、ようやく今書いているところです。
前提として、scalapropsとscala-nativeについて説明しておくと
- scalapropsは2年少し前に作って今もメンテしている、自作のproperty based testingのライブラリで (これ参照 Property Based Testing - scalapropsとscalacheckとその他色々 )
- scala-nativeは、LLVM使うことにより、scalaがJVMなしのネイティブで動く、というものです。まだ最初の version 0.1 が出てから半年も経っていない、比較的若いプロジェクトです
以前scalazでの対応については、簡単に書きました
それ以来、scala-native本体に色々細かいpull reqやissue報告したり、対応できそうなライブラリ(自作のmsgpackのやつ、argonautというjsonライブラリ、shapeless)で、いくつか対応させるなどもしています。
ここからscalapropsでの対応させようとした経緯や試行錯誤などをはなしていきます。 scalapropsでのscala-native対応も、ごく初期のscala-native 0.1時点からやろうとしたわけですが、
- 0.1時点では正規表現が全くないことにより、Stringのメソッドが動かなくて無理だった
- 0.2時点では、一部動くようになったが、再帰メソッドがバグるわりと致命的なものがあったり strange behavior List#corresponds · Issue #695 · scala-native/scala-native · GitHub ビルド時にメモリ大量に使って辛かったり、といった問題があり、また諦めた。
I have set "-Xmx4096m" but got OutOfMemoryError :(
— Kenji Yoshida (@xuwei_k) 2017年4月28日
- 0.3で、ようやく動くようになったので、対応させてリリースした(イマココ
という状態です。
さて、scala-nativeでは(いくつかリリース後すぐにバグが見つかったものの) 0.3.0 から、sbtのtest-interfaceをサポートしています。
test-interfaceについては、こちらも参照
しかし、scalapropsでのscala-native対応では、それを直接使っていません。なぜかというと、
- scala-nativeでは version 0.3 時点ではリフレクションをサポートしていない
- scalapropsは、 “object内のfield” = “テスト一覧” を、JVMやscala-jsではリフレクションで取得する実装になっている
- 既存のscalapropsのソース互換を保ったまま、かつscala-native 0.3 から提供されたtest-interfaceを使うのは、色々と相性が悪く厳しい
という理由です。結論としては、だいぶscala-native側がやった方法に近いのですが、微妙に違ったかたちで独自にscalapropsのsbt-pluginでコード生成する形式にしました
scala-native · scalaprops/sbt-scalaprops@522ca13 · GitHub
それ以外に考えた選択肢としては
- scalapropsの「フィールドを定義しただけでテスト定義」というインターフェイスを変える => 大幅にソース互換崩れるので、scala-nativeのためだけにやりたくない
- コード生成ではなく、コンパイラプラグインで、コンパイル時に “object内のfield” = “テスト一覧” を呼び出せるメソッドを生成する => ソース互換は保てるが、コンパイラプラグインはメンテが大変だしできるだけ避けたい
- scalametaのマクロアノテーションで “object内のfield” = “テスト一覧” を呼び出せるメソッドを生成 => アノテーション一つとはいえ、それぞれのテストのobjectにつけて回るの面倒
などですが、やめて、完全独自コード生成になってしまいました。とにかくそれ以外も含め何らかのかたちで、scala-native側が用意してくれたtest-interfaceの仕組みを使っておきたい気持ちがあるにはあったのですが、色々考えた末あきらめ、現在はそれらを 完全に独自にscalapropsのsbt pluginが上書きしています 。
これはこれでわりと邪道感あるので、将来的にリフレクションがscala-nativeでも可能になって、JVMやJSと同じ方式でテストできればいいのですが、今後のscala-nativeの動向や、自分の気分次第なので、どうなるのか不明です。
細かい点では他にも色々書きたいことがなくもないですが、blog書くの疲れるので、このあたりにしておきましょう。 しばらく飽きるまで、もっとscala-native試してバグ見つける、対応可能そうなライブラリへpull reqする、などの貢献続けるつもりです。
scala-nativeやることにより、
- まだまだ若いプロジェクトなので、わりとバグに頻繁に出会ったり
- JVMでは例外なのにscala-nativeではセグフォしたり、scala-nativeと比較してscala-jsの完成度の高さすごいなぁ
と思える、といった効果があるので、みなさんも積極的にscala-native試しましょう