I'm trying to get test coverage report of our Android application module and executing the testVariantBuildTypeUnitTest task.
Although all tests are passed, TEST-classNameTest.xml file contains error message below. This error is only given for methods contain calls Java from Kotlin. Is there any solution for this issue ?
**Caused by: java.lang.IllegalStateException: Cannot process instrumented class... Please supply original non-instrumented classes.
at org.jacoco.agent.rt.internal_f3994fa.core.internal.instr.InstrSupport.assertNotInstrumented(InstrSupport.java:238)
at org.jacoco.agent.rt.internal_f3994fa.core.internal.instr.ClassInstrumenter.visitField(ClassInstrumenter.java:56)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassVisitor.visitField(ClassVisitor.java:339)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassReader.readField(ClassReader.java:1111)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassReader.accept(ClassReader.java:713)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassReader.accept(ClassReader.java:401)
at org.jacoco.agent.rt.internal_f3994fa.core.instr.Instrumenter.instrument(Instrumenter.java:90)
at org.jacoco.agent.rt.internal_f3994fa.core.instr.Instrumenter.instrument(Instrumenter.java:108)
We have multimodule android project, because of this we're using this custom Jacoco task to get coverage report:
project.afterEvaluate {
(android.hasProperty('applicationVariants')
? android.'applicationVariants'
: android.'libraryVariants')
.all { variant ->
def variantName = variant.name
def unitTestTask = "test${variantName.capitalize()}UnitTest"
def jacocoReportName = unitTestTask + project.name
tasks.create(name: jacocoReportName, type: JacocoReport, dependsOn: [
"$unitTestTask"
]) {
group = "Reporting"
description = "Generate Jacoco coverage reports for the ${variantName.capitalize()} build"
reports {
html.enabled = true
xml.enabled = true
}
def fileFilter = [
// data binding
'android/databinding/**/*.class',
'**/android/databinding/*Binding.class',
'**/android/databinding/*',
'**/androidx/databinding/*',
'**/databinding',
'**/BR.*',
// android
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*'
]
def javaClasses = fileTree(dir: variant.javaCompileProvider.get().destinationDir,
excludes: fileFilter)
def kotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/${variantName}",
excludes: fileFilter)
classDirectories.setFrom(files([
javaClasses,
kotlinClasses
]))
def variantSourceSets = variant.sourceSets.java.srcDirs.collect { it.path }.flatten()
sourceDirectories.setFrom(project.files(variantSourceSets))
if (isAndroidLibrary(project)) {
executionData(files([
"${projectDir}/jacoco.exec"
]))
}else{
executionData(files([
"$project.buildDir/jacoco/${project.name}.exec"
]))
}
}
}
}
Regards,
The problem most likely lies in your module level build.gradle file. You need to remove testCoverageEnabled or set it to false.
android {
...
buildTypes {
release {
minifyEnabled true
}
debug {
// testCoverageEnabled true
minifyEnabled false
}
}
Firstly I've been struggling with unit test coverage on android with JaCoCo for years and repeatedly encountered the issues multiple times as the AGP (Android Gradle Plugin) team enhanced the AGP (e.g. Similar issue) therefore this solution is subject to change in future releases of the AGP.
Come the release of AGP 4.1 the AGP team changed the way the plugin deals with unit tests. I raised an enter link description here issue with the AGP team and they explained that the plugin (AGP 4.1+) does it's own form of instrumentation to get the coverage which is undoubtedly incompatible with JaCoCo. It was in this issue where the solution was highlighted to me.
Note 1: This issue is not documented in the DSL documentation!
Note 2: It should also be noted that as of when I wrote this AGP 7 is in development where the same issue occurs.
This solution will cause a problem generating coverage for instrumented tests as the AGP generates coverage for instrumented tests (in the form of a .ec file) meaning to get instrumented test coverage you must have testCoverageEnabled true
.
As a workaround you could:
task jacocoCombinedUnitTestAndroidTestReport(type: JacocoReport) {
group = "Reporting"
reports {
xml.enabled = true
html.enabled = true
}
def mainSrc = "$project.projectDir/src/main/java"
def releaseKotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/release", excludes:["com/example/ui**"])
def debugKotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug/com/example/ui/")
def releaseJavaClasses = fileTree(dir: "${buildDir}/intermediates/javac/release", excludes: ["com/example/ui**"])
def debugJavaClasses = fileTree(dir: "${buildDir}/intermediates/javac/debug/com/example/ui/" )
def mainClasses = releaseKotlinClasses + debugKotlinClasses
def javaClasses = releaseJavaClasses + debugJavaClasses
sourceDirectories.from = files([mainSrc])
classDirectories.from = files([mainClasses, javaClasses])
executionData.from = fileTree(dir: buildDir, include: ["jacoco/testReleaseUnitTest.exec",
"outputs/code_coverage/debugAndroidTest/connected/**.ec"])
}
Please note this is only valid as of when this answer was written and hopefully Google will fix the issue in the future.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With