sbt android plugin でテストできなかった件
はじめに
sbt android plugin とは、Scala で Android アプリを書くための素敵な方法だと聞き、特に考えもなく、どちらも経験が浅いのに、手を出しました。
最初のアプリは大した問題もなく、スムーズにリリースできました。しかし、機能追加に伴い、テストを書きたい、となったときに、この android plugin がサポートしている tests サブプロジェクトがうまく動きませんでした。
再現
https://github.com/jberkel/android-plugin/wiki/getting-started を参考に、プロジェクトを作成します。再現のためにすべてデフォルトを選択しました。
$ brew install giter8 $ g8 jberkel/android-app Template for Android apps in Scala package [my.android.project]: name [My Android Project]: main_activity [MainActivity]: scala_version [2.9.1]: api_level [10]: useProguard [true]: Applied jberkel/android-app.g8 in my-android-project
プロジェクトディレクトリに移動して、sbt を起動し、パッケージを作ります。sbt は HomeBrew などでインストールしておいてください。
$ cd my-android-project $ export ANDROID_HOME=/Applications/android-sdk-macosx $ sbt >android:package-debug
エミュレータを起動します。
> android:emulator-start my_avd > android:start-emulator
問題なくサンプルプロジェクトが起動します。
テストプロジェクトの存在を知る
giter8 で作られたサンプルプロジェクトには、「tests」というサブプロジェクトが含まれています。https://github.com/jberkel/android-plugin/wiki/Building-Android-Test-Projects を参考に実行してみましょう。
> android:package-debug > android:install-emulator > tests/android:package-debug > tests/android:install-emulator > tests/android:test-emulator
このようになります。
[info] [info] my.android.project.tests.ActivityTests:INSTRUMENTATION_RESULT: shortMsg=java.lang.IllegalAccessError [info] INSTRUMENTATION_RESULT: longMsg=java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation [info] INSTRUMENTATION_CODE: 0 [success] Total time: 3 s, completed 2012/04/11 16:59:14
何が success だ、と言いたくなります。
ddms でログなどを見た結果、Java も Scala もにわかな私の認識では、TypedResource などのクラスの解決がうまくいっていない、といったところでした。
打開
5日ほど試行錯誤したのですが、何とかテストを実行させられた手順が以下になります。
まず android plugin に手をいれるので github から落としてくる。
$ cd .. $ git clone git://github.com/jberkel/android-plugin.git $ cd android-plugin
AndroidInstall.scala を編集(対象コミットは 6f6e4b9)
--- a/src/main/scala/AndroidInstall.scala +++ b/src/main/scala/AndroidInstall.scala @@ -69,8 +69,7 @@ object AndroidInstall { classesMinJarPath, libraryJarPath, manifestPackage, proguardOption) => if (useProguard) { val optimizationOptions = if (proguardOptimizations.isEmpty) Seq("-dontoptimize") else proguardOptimizations - val manifestr = List("!META-INF/MANIFEST.MF", "R.class", "R$*.class", - "TR.class", "TR$.class", "library.properties") + val manifestr = List("!META-INF/MANIFEST.MF", "library.properties") val sep = JFile.pathSeparator val inJars = ("\"" + classDirectory.absolutePath + "\"") +: proguardInJars.map("\""+_+"\""+manifestr.mkString("(", ",!**/", ")"))
72行目あたり、manifestr から "R.class", "R$*.class", "TR.class", "TR$.class" を削除します。このへんは ProGuard という機構の設定をしているようです。
編集したら、publish-local します。
$ sbt publish-local
サンプルプロジェクトに戻り、project/plugins.sbt を編集します。
--- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns) -addSbtPlugin("org.scala-sbt" % "sbt-android-plugin" % "0.6.1") +addSbtPlugin("org.scala-sbt" % "sbt-android-plugin" % "0.6.2-SNAPSHOT")
sbt-android-plugin のバージョンを 0.6.2-SNAPSHOT に変えることで、さっき編集したバージョンを読みにいかせます(この説明は自信がない…)。
編集したら、もう一度テストを実行します。
> reload > clean > tests/clean > android:package-debug > android:install-emulator > tests/android:package-debug > tests/android:install-emulator > tests/android:test-emulator
今度はうまくいくはず。
[info] [info] my.android.project.tests.ActivityTests:. [info] my.android.project.tests.AndroidTests:.. [info] Test results for InstrumentationTestRunner=... [info] Time: 1.509 [info] [info] OK (3 tests) [info] [info] [success] Total time: 4 s, completed 2012/04/11 17:25:30