Java – No static method deleteRecursively in androidTest when proguard is enabled (Ljava/io/File;)

No static method deleteRecursively in androidTest when proguard is enabled (Ljava/io/File;)… here is a solution to the problem.

No static method deleteRecursively in androidTest when proguard is enabled (Ljava/io/File;)

I’m trying to enable the obfuscator in my android tests. But I ran into a strange problem :

java.lang.NoSuchMethodError: No static method deleteRecursively(Ljava/io/File;)Z in class Lkotlin/io/FilesKt; or its super classes (declaration of 'kotlin.io.FilesKt' appears in /data/app/org.walleth.offline-BAciL8erjxU-sHGjQe6uQg==/base.apk!classes2.dex)
at org.ligi.trulesk.RulesKt.doBefore(Rules.kt:82)
at org.ligi.trulesk.RulesKt.access$doBefore(Rules.kt:1)
at org.ligi.trulesk.TruleskIntentRule.beforeActivityLaunched(Rules.kt:58)
at android.support.test.rule.ActivityTestRule.launchActivity(ActivityTestRule.java:351)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:525)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:384)
at org.ligi.trulesk.AppReplacingRunnerBase.onStart(AppReplacingRunnerBase.kt:19)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2145)

Proguard works on the release version – not sure why it’s so aggressive in android testing. Ideally, the instrument class would not be deleted at all.

Solution

I think the core problem is that androidTest is a different compilation unit. app/src/androidTest is assembled into app-debug-androidTest.apk while app/src/main is assembled into app-debug .apk. Therefore, during assembleDebug, Proguard does not include app/src/androidTest in the build “tree shaking” usage graph because it is not available on the classpath. In practice, this means that only the application code referenced in the test is removed.

The topic is related to “Should I change the visibility from private to public just to test access?” “Related dilemmas. Depending on the type of test you’re writing, you might consider:

  • Always keep the application additional entry point (for testing) available to the user release .apk
  • Compile the main APK differently when you run the test, and again accept the risk of sending the user code that is not exactly the same as the code that the test runs

Unfortunately, at the moment I don’t know of any automatic magic solutions (similar to all-open plugins). However, the missing method can be addressed on a case-by-case basis.

buildTypes {
    debug {
        minifyEnabled true

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt', 'proguard-project-ext.txt'
        testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-test-project.txt'
    }
}      
  • proguard-project.txt – The configuration of app/src/main and its implementation dependencies
  • proguard-project-ext.txt – The configuration of app/src/main with -keep instructions for each method/field used is only in tests that were “mistakenly” removed by the obfuscator. I prefer to save separately so that it can be conditionally deleted when publishing to Google Play
  • proguard-test-project.txtConfiguration of app/src/androidTest and its androidTestImplementation dependencies. Usually contains -dontwarn net.bytebuddy.**, etc. Although note that the test apk does not support Multidex, it is also possible to reach the 65K limit by including many dependencies of the method.

Check android/platform/tools/base/studio-master-dev/./build-system/integration-test/test-projects/minify for implementation references.

Related Problems and Solutions