Leakcanary: java.lang.NoSuchMethodError on obfuscated instrumentation tests

Created on 30 Jan 2020  路  8Comments  路  Source: square/leakcanary

Description

FailTestOnLeakRunListener does not appear to function at all in instrumentation tests when obfuscation is enabled.

Steps to Reproduce

Run the following command on master (revision 2aacb8b46) of this project:
~./gradlew leakcanary-android-example:connectedCheck -Pminify~

Expected behavior:

Tests pass

Version Information

  • LeakCanary version: 2.1
  • Android OS version: 10.0
  • Gradle version: 5.6.2

Additional Information

Tests fail with this:

Starting 0 tests on Nexus_5_API_Q(AVD) - 10
Tests on Nexus_5_API_Q(AVD) - 10 failed: There was 1 failure:
1) Fatal exception when running tests
java.lang.NoSuchMethodError: No static method copy$default(Lc/a$a;ZZZZJILjava/lang/Object;)Lc/a$a; in class Lc/a$a; or its super classes (declaration of 'c.a$a' appears in /data/app/com.example.leakcanary-QAdW6_QGYEoCz4zbR2QTkg==/base.apk)
at leakcanary.InstrumentationLeakDetector$Companion.updateConfig(InstrumentationLeakDetector.kt:195)
at leakcanary.FailTestOnLeakRunListener.testRunStarted(FailTestOnLeakRunListener.kt:92)
at org.junit.runner.notification.SynchronizedRunListener.testRunStarted(SynchronizedRunListener.java:35)
at org.junit.runner.notification.RunNotifier$1.notifyListener(RunNotifier.java:91)
at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:72)
at org.junit.runner.notification.RunNotifier.fireTestRunStarted(RunNotifier.java:88)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)

FAILURES!!!
Tests run: 0,  Failures: 1
bug

Most helpful comment

Keeper 0.2 fixes this issue!

All 8 comments

Can you reproduce that with the sample app? We added support for obfuscation to it.

Isn't leakcanary-android-sample the sample app? Oh I guess I typoed above. The command is:
./gradlew leakcanary-android-sample:connectedCheck -Pminify

Reproduced with ./gradlew leakcanary-android-sample:connectedCheck -Pminify :

1) Fatal exception when running tests
java.lang.NoSuchMethodError: No static method copy$default(Lc/a$a;ZZZZJILjava/lang/Object;)Lc/a$a; in class Lc/a$a; or its super classes (declaration of 'c.a$a' appears in /data/app/com.example.leakcanary-1/base.apk)
at leakcanary.InstrumentationLeakDetector$Companion.updateConfig(InstrumentationLeakDetector.kt:195)
at leakcanary.FailTestOnLeakRunListener.testRunStarted(FailTestOnLeakRunListener.kt:92)
at org.junit.runner.notification.SynchronizedRunListener.testRunStarted(SynchronizedRunListener.java:35)
at org.junit.runner.notification.RunNotifier$1.notifyListener(RunNotifier.java:91)
at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:72)
at org.junit.runner.notification.RunNotifier.fireTestRunStarted(RunNotifier.java:88)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932)

Looking into this, InstrumentationLeakDetector is in the test APK and the AppWatcher.Config.copy is in the main APK. @adamfit pointed me to https://slackhq.github.io/keeper/ and it looks like it's exactly solving that issue.

I need to double check, but if that's the case I don't think LeakCanary can do anything about it except documenting. Otherwise we'd have to make it keep all APIs, which isn't great either.

I set up Keeper but it did not infer the proper rules, most likely because of https://github.com/slackhq/keeper/issues/18#issuecomment-580522161

Gave this a spin and unfortunately I think keeper is working correctly here. That dependency is an androidTest dependency only, not in the main APK (so keeper's use here is irrelevant). I was able to get Py's repro branch working with the below patch.

Index: leakcanary-android-sample/build.gradle
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- leakcanary-android-sample/build.gradle  (revision 59bb7270fc6dd9db3bc5e5f3e5b8e36695fb7310)
+++ leakcanary-android-sample/build.gradle  (date 1580694279000)
@@ -47,17 +47,21 @@
   }
   buildTypes {
+    release {
+      signingConfig signingConfigs.debug
+      minifyEnabled true
+      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
+    }
+    releaseTest {
+      initWith release
+      debuggable true
+      matchingFallbacks = ['release']
+    }
+
     // Build with ./gradlew leakcanary-android-sample:installDebug -Pminify
     if (project.hasProperty('minify')) {
-      debug {
-        minifyEnabled true
-        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-      }
-    } else {
-      debug
+      testBuildType "releaseTest"
     }
-
-    release
   }
   dexOptions {

We chatted some more and it seems likely that defining release builds wouldn't trigger this bug.

Keeper 0.2 fixes this issue!

Was this page helpful?
0 / 5 - 0 ratings