Android Gradle PluginでJaCoCoを有効にしてテストするとjava.lang.VerifyError発生

AndroidFormEnhancerは、Eclipseでの利用も可能にしているものの基本的にはAndroid Studioで開発し、Gradleでビルドしている。
テストカバレッジを上げるために少しでも多くのテストを書こうとしているがconnectedAndroidTestタスクを実行しているときに特定のクラスでjava.lang.VerifyErrorが発生する。
(未解決です)

com.androidformenhancer.helper.ActivityFormHelperTest > testInit[test(AVD) - 4.2.2] FAILED
java.lang.VerifyError: com/androidformenhancer/internal/ValidationManager
at com.androidformenhancer.helper.FormHelper.setValidationManager(FormHelper.java:356)
:library:connectedAndroidTest FAILED

FAILURE: Build failed with an exception.

バージョン情報

各種バージョンは以下の通り。

  • Android Studio 0.5.7
  • Gradle 1.10 (Gradle Wrapper)
  • com.android.tools.build:gradle:0.10.+
  • com.android.support:support-v4:19.0.1

VerifyErrorの詳細

上記の通りメッセージにはクラス名しか含まれていないので、なぜVerifyErrorが発生したのかが分からない。
そのクラスのコンストラクタを呼び出すだけで発生し、中身が空のデフォルトコンストラクタでも発生するのでこのクラス内の処理が問題なのではなく、クラスの構造や依存関係(importなど)が関係しているのかもしれない。

外部ライブラリが原因か?

当初は原因の一つが、android-support-v4ライブラリに依存していることのように見えた。

このライブラリが問題、というよりは外部jarファイルに依存したライブラリプロジェクトだと、androidTestなどのテストタスクが失敗する模様。

よく考えてみれば、後方互換性のためのライブラリを含めているとこれが障害で導入できないプロジェクトもありそうなのでなるべく切り離したプロジェクト構成として見直すことにした。

実際、問題の起きたクラスがandroid-support-v4ライブラリに依存しないように修正すると、そのクラスでは発生しなくなった。

しかし、そのクラスが読み込む別のクラスで発生。
このクラスはandroid-support-v4もその他の外部ライブラリにも依存していない。
どうやら外部ライブラリが原因ではないらしい。

Verifierの調整

AndroidでVerifyErrorというと、ADTのバージョンによってはjarファイルがエクスポートされない設定になっているだとかの情報が見当たるが、今回のケースには該当しない。

CoberturaでVerifyErrorにてカバレッジが取得できない現象という記事を見つけ、同様にJDK7関連の問題か?と以下をgradle-wrapper.propertiesに追記して実行してみる。

org.gradle.jvmargs=-XX:-UseSplitVerifier

しかし失敗。。。
実行時に与えるように以下も試したがやはり失敗。

GRADLE_OPTS="-XX:-UseSplitVerifier" ./gradlew :library:connectedCheck

でもカバレッジ取得が関係していそうな気が。
そこで、gradle.propertiesから以下の記述を除去してみる。

buildTypes {
    debug {
        testCoverageEnabled = true
    }
}

すると、なんとテストが成功!
しかしカバレッジを上げるためのテストなのにカバレッジを取らない場合でしか実行できないのでは意味がない。。。
JaCoCo(もしくはAndroid Gradle Plugin?)が問題である可能性は高い。
以前のエントリでのように別のGradleファイルに記述することでうまく回避したいが、、、
前回はカバレッジ取得+カバレッジ送信だったので分けられたが今回は同一の操作(テスト&カバレッジ取得)なので分割しようがない。

さらに見ていくと、Javassist関連でもVerifyErrorの情報が見つかる。
https://code.google.com/p/powermock/issues/detail?id=355
EMMAなどのキーワードで探してもやはり色々見つかる。

リフレクション関連の問題なのだろうか?
Android Gradle PluginがDaggerには対応していないということもあるし。。
(以下、Daggerに関する記述の引用)

Known issue: This is not compatible with using Dagger.

DIなのでリフレクション的なことはしているのだろうと思い実際、Daggerのリポジトリ内でjava.lang.reflectを探してみるといくらかヒットする。

今回問題の起きているAndroidFormEnhancerもリフレクションを多用している。

ちなみにandroid-support-v4ライブラリ内にもjava.lang.reflectパッケージの利用箇所がある。

他に試したこと

…ということでVerifyError問題は解決せず。
カバレッジは35%のまま。。。
リフレクション+カバレッジ計測の問題だとするとかなり根が深そう。
上記の他には以下のことも試してみた。

  • JAVA_OPTS-XX:-UseSplitVerifierを設定して実行→失敗
  • GRADLE_OPTS-XX:-UseSplitVerifierを試す→失敗
  • -XX:+FailOverToOldVerifierを試す→失敗
  • -Xverify:noneを試す→失敗
  • JaCoCoのバージョンを下げる→失敗
  • JDK6で実行する(DockerでOpenJDK6を入れてテスト)→失敗

ここまで書いて気づいたが、connectedAndroidTestで発生しているのだからGradleを実行するJVMに問題があるのではなく端末上のVM(DalvikVM)のVerifierに問題がある・・?

今回はここまで。

© 2010 ksoichiro