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 ofapp/src/main
and itsimplementation
dependenciesproguard-project-ext.txt
– The configuration ofapp/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 Playproguard-test-project.txt
–Configuration of app/src/androidTest
and itsandroidTestImplementation
dependencies. Usually contains-dontwarn net.bytebuddy.**
, etc. Although note that the test apk does not support Multidex, it is also possible to reach the65K
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.