12
votes

kotlin verion: 1.3.61

Android Gradle Plugin: 3.5.3

JaCoCo Version: 0.8.4.201905082037 (default android gradle version)

project: https://github.com/goldy1992/Mp3Player/tree/feature/issue-112/migrate-to-kotlin

I have a multi module gradle project with: 2 flavours: full, automation 2 build types: release, debug

I'm in the process of migrating everything to kotlin.

Module structure

  • commons [android library] (works as expected with test coverage)
  • client-test-support (used for test implementations of activities)
  • client [android library] (no coverage) -> depends on commons, client-test-support
  • service-test-support (used for test implementations of activities)
  • service [android library] (no coverage) -> depends on commons, service-test-support
  • app [android application] no tests

For the context of this posts, I'm only interested in unit tests

The tests run as expected however no coverage is generated due to a SOME (not all) of the kotlin compiled classes being recognised as "instrumented classes"

The errors appear just as the tests are about to being where the following is printed out for many classes:

    java.lang.instrument.IllegalClassFormatException: Error while instrumenting com/github/goldy1992/mp3player/client/views/viewholders/MediaPlayerTrackViewHolder.
        at org.jacoco.agent.rt.internal_035b120.CoverageTransformer.transform(CoverageTransformer.java:93)
        at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
        at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.Class.getDeclaredFields0(Native Method)
        at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
        at java.lang.Class.getDeclaredFields(Class.java:1916)
        at org.junit.runners.model.TestClass.getSortedDeclaredFields(TestClass.java:77)
        at org.junit.runners.model.TestClass.scanAnnotatedMembers(TestClass.java:70)
        at org.junit.runners.model.TestClass.<init>(TestClass.java:57)
        at org.junit.runners.ParentRunner.createTestClass(ParentRunner.java:88)
        at org.junit.runners.ParentRunner.<init>(ParentRunner.java:83)
        at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
        at org.robolectric.internal.SandboxTestRunner.<init>(SandboxTestRunner.java:71)
        at org.robolectric.RobolectricTestRunner.<init>(RobolectricTestRunner.java:101)
        at org.robolectric.RobolectricTestRunner.<init>(RobolectricTestRunner.java:96)
        at sun.reflect.GeneratedConstructorAccessor5.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
        at org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder$DefensiveAnnotatedBuilder.buildRunner(DefensiveAllDefaultPossibilitiesBuilder.java:113)
        at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
        at org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder.runnerForClass(DefensiveAllDefaultPossibilitiesBuilder.java:56)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.vintage.engine.discovery.TestClassRequestResolver.createRunnerTestDescriptor(TestClassRequestResolver.java:55)
        at org.junit.vintage.engine.discovery.VintageDiscoverer.lambda$discover$0(VintageDiscoverer.java:53)
        at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
        at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
        at java.util.Iterator.forEachRemaining(Iterator.java:116)
        at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
        at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
        at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
        at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
        at org.junit.vintage.engine.discovery.VintageDiscoverer.discover(VintageDiscoverer.java:55)
        at org.junit.vintage.engine.VintageTestEngine.discover(VintageTestEngine.java:62)
        at org.junit.platform.launcher.core.DefaultLauncher.discoverEngineRoot(DefaultLauncher.java:168)
        at org.junit.platform.launcher.core.DefaultLauncher.discoverRoot(DefaultLauncher.java:155)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
        at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:102)
        at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:82)
        at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:78)
        at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
        at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
        at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
        at com.sun.proxy.$Proxy2.stop(Unknown Source)
        at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:132)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
        at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
        at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
        at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:412)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
        at java.lang.Thread.run(Thread.java:748)
    Caused by: java.io.IOException: Error while instrumenting com/github/goldy1992/mp3player/client/views/viewholders/MediaPlayerTrackViewHolder.
        at org.jacoco.agent.rt.internal_035b120.core.instr.Instrumenter.instrumentError(Instrumenter.java:158)
        at org.jacoco.agent.rt.internal_035b120.core.instr.Instrumenter.instrument(Instrumenter.java:108)
        at org.jacoco.agent.rt.internal_035b120.CoverageTransformer.transform(CoverageTransformer.java:91)
        ... 86 more
    Caused by: java.lang.IllegalStateException: Cannot process instrumented class com/github/goldy1992/mp3player/client/views/viewholders/MediaPlayerTrackViewHolder. Please supply original non-instrumented classes.
        at org.jacoco.agent.rt.internal_035b120.core.internal.instr.InstrSupport.assertNotInstrumented(InstrSupport.java:237)
        at org.jacoco.agent.rt.internal_035b120.core.internal.instr.ClassInstrumenter.visitField(ClassInstrumenter.java:55)
        at org.jacoco.agent.rt.internal_035b120.asm.ClassVisitor.visitField(ClassVisitor.java:287)
        at org.jacoco.agent.rt.internal_035b120.asm.ClassReader.readField(ClassReader.java:906)
        at org.jacoco.agent.rt.internal_035b120.asm.ClassReader.accept(ClassReader.java:683)
        at org.jacoco.agent.rt.internal_035b120.asm.ClassReader.accept(ClassReader.java:400)
        at org.jacoco.agent.rt.internal_035b120.core.instr.Instrumenter.instrument(Instrumenter.java:88)
        at org.jacoco.agent.rt.internal_035b120.core.instr.Instrumenter.instrument(Instrumenter.java:106)
        ... 87 more

To try and get to the root of the problem I am just running the client unit tests

Client build.gradle

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'de.mannodermaus.android-junit5'
apply plugin: 'jacoco'

android {

    compileSdkVersion TARGET_SDK_VERION
    buildToolsVersion BUILD_TOOLS_VERSION

    defaultConfig {
        minSdkVersion MIN_SDK_VERSION
        targetSdkVersion TARGET_SDK_VERION
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles 'consumer-rules.pro'
        vectorDrawables.useSupportLibrary = true
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            debuggable true
            minifyEnabled false
            testCoverageEnabled = true
        }
    }
    flavorDimensions 'default'
    productFlavors {
        full {
            dimension = 'default'
        }
        automation {
            dimension = 'default'
        }
    }

    compileOptions {
        incremental = false
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions { jvmTarget = "1.8" }


    testOptions {
        execution 'ANDROID_TEST_ORCHESTRATOR'
        animationsDisabled true

        unitTests {
            includeAndroidResources = true
            returnDefaultValues = true
        }

        unitTests.all {
            testLogging {
                events "passed", "skipped", "failed", "standardOut", "standardError"
                outputs.upToDateWhen {false}
                showStandardStreams = true
            }
        }
    }

    sourceSets {
        main.java.srcDirs += 'src/main/java'
        test.java.srcDirs += 'src/test/java'
        androidTest.java.srcDirs += 'src/androidTest/java'
    }
}

dependencies {
 implementation project(path: ':commons')
 testApi project(":client:testsupport")

 /* MORE DEPENDENCIES NOT RELEVANT TO ISSUE */
}

It may be worth noting that I am using Dagger 2.25.2

Execution data dir: ${projectRootDir}/client/build/jacoco/testFullDebugUnitTest.exec

Source dir: ${projectRootDir}/client/src/main/java

class dir: ${projectRootDir}/client/build/intermediates/javac/fullDebug/classes/, ${projectRootDir}/client/build/tmp/kotlin-classes/fullDebug/

UPDATE 1: From making a new branch and and renaming all the classes and test classes in the client module to CLASS_NAME.kt.old I can confirm that there seems to be no connection between the IllegalClassFormatException and the no coverage.I will therefore update the questions to be the following

UPDATE 2: After further experimenting with update 1 it seems that the exceptions go away and coverage is calculated as expected WHEN there is no dependency on the sub (test) module, i.e. remove the line testApi project(":client:testsupport")from the gradle file. The problem is that I need this testsupportmodule in order to execute my activity tests (which are currently not run because they are in a .old file as per update 1 ^^.

Questions updated as per udate 2 How can I configure JaCoCo to support sub module test implementation of android classes as suggested by the robolectric team in this pull request.

Will update post if more information is required

1
Do you have any further update over this problem? I'm facing the same problem using jacoco 0.8.6 when trying to test a module other than :app.Cassio Landim

1 Answers

5
votes

I just faced the same bug.

After a little investigation, it seems like it is a bug in the Android Gradle Plugin, and it only happens when using com.android.library.

I have reported it and more details here: https://issuetracker.google.com/issues/178015739

While it isn't fixed by the Android team, a workaround is to configure

jacoco {
    toolVersion = "0.7.9"
}

in the build.gradle

It doesn't prevent the error from being logged, but makes it so that the coverage data is collected properly.