Detox - DetoxManager can not find kotlin/jvm/functions/Function1 at runtime

Viewed 1156

To reproduce the issue I'm facing I used the react-native example project demo-react-native from `Detox.

After installing the packages with yarn I executed the 2 commands to run the test:

1) cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..

2) npx detox test -l error --configuration android.emu.release

So far everything works as expected but as soon I add the dependency of the Kotlin standard library in the android/app/build.gradle file like that

dependencies {
  ...
  androidTestImplementation('com.wix:detox:+')
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71" // <-- line added
}

and run the 2 steps again I get the following error

04-05 20:21:06.709 16552 16615 E AndroidRuntime: FATAL EXCEPTION: com.wix.detox.manager
04-05 20:21:06.709 16552 16615 E AndroidRuntime: Process: com.test.app, PID: 16552
04-05 20:21:06.709 16552 16615 E AndroidRuntime: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/functions/Function1;
04-05 20:21:06.709 16552 16615 E AndroidRuntime:    at com.wix.detox.Detox$1.run(Detox.java:134)
04-05 20:21:06.709 16552 16615 E AndroidRuntime:    at java.lang.Thread.run(Thread.java:764)
04-05 20:21:06.709 16552 16615 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "kotlin.jvm.functions.Function1" on path: DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/system/framework/android.test.mock.jar", zip file "/data/app/com.test.app.test-xbA2GW9WwS_BxzRNPa2waQ==/base.apk", zip file "/data/app/com.test.app-g_-tD026mxddTh82TmdYGg==/base.apk"],nativeLibraryDirectories=[/data/app/com.test.app.test-xbA2GW9WwS_BxzRNPa2waQ==/lib/x86, /data/app/com.test.app-g_-tD026mxddTh82TmdYGg==/lib/x86, /data/app/com.test.app.test-xbA2GW9WwS_BxzRNPa2waQ==/base.apk!/lib/x86, /data/app/com.test.app-g_-tD026mxddTh82TmdYGg==/base.apk!/lib/x86, /system/lib]]
04-05 20:21:06.709 16552 16615 E AndroidRuntime:    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
04-05 20:21:06.709 16552 16615 E AndroidRuntime:    at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
04-05 20:21:06.709 16552 16615 E AndroidRuntime:    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)

I have tried to load the JVM plugin in the android/build.gradle file as described here but it doesn't help.

What else can I do to make org.jetbrains.kotlin.jvm work in that project?

Thank you in advance.

Edit 1

I added kotlin.jvm.functions.Function1 to the MainApplication.java to test whether the Kotlin JVM is actually accessible by the project by adding the following code

...
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;

public class MainApplication extends Application implements ReactApplication {
    private Function1<Throwable, String> errorParseFn = new Function1<Throwable, String>() {
        @Override
        public String invoke(Throwable t) {
            return Log.getStackTraceString(t);
        }
    };

    ...

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
        Log.d("@@@@@@", "onCreate");
        Log.d("@@@@@@", errorParseFn.invoke(new Exception("Test Exception message")));
    }
}

after I ran npx detox test -l error --configuration android.emu.release once again I could see the logs in the console.

04-06 21:24:06.715 23836 23836 D @@@@@@  : onCreate
04-06 21:24:06.716 23836 23836 D @@@@@@  : java.lang.Exception: Test Exception message 

That means that the app itself has access to the kotlin.jvm, which means the gradle setup is correct.

Has anyone an idea how DetoxManager.java can access the Kotlin JVM at runtime?

2 Answers

I finally have time to post the solution.

After researching the meaning of the log

Note: kotlin.jvm.internal.Reflection: can't find dynamically referenced class kotlin.reflect.jvm.internal.ReflectionFactoryImpl
Note: kotlin.coroutines.jvm.internal.DebugMetadataKt accesses a declared field 'label' dynamically
...
Note: there were 1 references to unknown classes.
      You should check your configuration for typos.
      (http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass)

I found that Proguard is removing Kotlin dependencies.

To prove the hypothesis I commented out the lines regarding proguard in the app/build.gradle of the demo-react-native

buildTypes {
  release {
    // minifyEnabled true
    // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    // proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"

    signingConfig signingConfigs.release
  }
}

and it worked as expected.

That meant I had to create my own buildType just for the E2E test and include proguard only for the release build

...
signingConfigs {
  e2e_release {
    storeFile file("keystore.e2e.jks")
    storePassword "987654321"
    keyAlias "e2e_key"
    keyPassword "987654321"
  }
  release {
    storeFile file("keystore.jks")
    storePassword "12345678"
    keyAlias "key0"
    keyPassword "12345678"
  }
buildTypes {
  e2eRelease {
    initWith release
    matchingFallbacks = ['release']
    signingConfig signingConfigs.e2e_release
  }
  release{}
  if(gradle.startParameter.taskNames.contains("assembleRelease")) {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
    signingConfig signingConfigs.release
  }
}

The only thing left was to adjust the path and build command in the package.json file like that

"android.emu.e2e": {
  "binaryPath": "android/app/build/outputs/apk/e2eRelease/app-e2eRelease.apk",
  "build": "cd android && RN_SRC_EXT=e2e.js ./gradlew app:assembleE2ERelease  app:assembleE2EReleaseAndroidTest -DtestBuildType=e2eRelease -x bundleE2eReleaseJsAndAssets && cd ..",
  "type": "android.emulator",
  "device": {
    "avdName": "Nexus_6P_API_28"
  }
}

Please note, the folder outputs/apk/e2eRelease have to be created manually before running ./gradlew assembleE2ERelease assembleE2EReleaseAndroidTest -DtestBuildType=e2eRelease

This works very well but let me know if there is a better solution.

Cheers

I was able to mitigate the same errors by adding the following configuratuon in proguard file.

-dontobfuscate



-keep class kotlin.** { *; }

-keep class kotlin.Metadata { *; }

-dontwarn kotlin.**
Related