Reputation: 10604
There is an issue with JaCoCo and the MultiRelease JAR files. Since the same class name exist on two places, JaCoCo complains:
Caused by: java.lang.IllegalStateException: Can't add different class with same name: jodd/core/JavaBridge
at org.jacoco.core.analysis.CoverageBuilder.visitCoverage(CoverageBuilder.java:107)
at org.jacoco.core.analysis.Analyzer$1.visitEnd(Analyzer.java:96)
How we can tell JaCoCo (in Gradle) to skip the classes from META-INF path? OR to behave like it should (use correct class and ignoring other versions), depending on JVM version?
Upvotes: 5
Views: 1147
Reputation: 1158
I had the same problem on Jacoco 0.8.8 (I guess they haven't fixed it yet). But I use maven, not gradle, so even though the accepted answer is correct, it was very hard for me to follow. First, files should be excluded in the report goal, not in the prepare-agent goal. That was not at all obvious to me and took careful reading of the maven jacoco help which can be seen using the following command
mvn help:describe -Dplugin=org.jacoco:jacoco-maven-plugin -Ddetail
Second, it wasn't obvious to me whether the exclude value was a path or a package reference and, if a path, what kind of path. By experimenting I found it's a path relative to the target/classes folder. Also note that foo/* excludes all the files in the foo folder. To exclude all files recursively under foo use foo/**/*. Based on all that this is what my unit test report goal looks like.
<!-- Use unit test coverage data to generate report -->
<execution>
<id>after-unit-tests-generate-report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Exclude alternate versions for multi-release modules-->
<excludes>
<exclude>META-INF/**/*</exclude>
</excludes>
<dataFile>${jacoco.data.file.ut}</dataFile>
<outputDirectory>${jacoco.report.folder.ut}</outputDirectory>
</configuration>
</execution>
That code excludes all the files under target/classes/META-INF. In other words, all the other versions besides the base. I was worried that my tests use Java 11 but my base is Java 8, but my coverage results seem correct.
Note the use of properties jacoco.data.file.ut and jacoco.report.folder.ut. Those are defined earlier in my pom file and otherwise are not relevant to this discussion. Also note, this is defined in the parent pom of a project with lots of child modules. Even though it is not inside a pluginManagement tag (only a plugins tag) it still applies for all the children.
Upvotes: 2
Reputation: 31918
JaCoCo doesn't yet provide support for Java 9 Multi-Release JAR Files.
This seems to be in their plans though as tracked at jacoco/issues#407.
Upvotes: 1
Reputation: 10604
As explained by @nullpointer, JaCoCo doesn't support Multi-Release JAR Files.
My workaround is to ignore the versions classes. I was not able to ignore just the class by explicitly set its name, it looks like JaCoCo is scanning all of them and then only later applies the filters for exclusion (but maybe I am wrong).
Therefore, the only way to remove versions classes was to exclude all resources - since they are not used anyway. Like this:
task codeCoverage(type: JacocoReport) {
executionData fileTree("${buildDir}/jacoco/").include("*.exec")
//sourceSets it.sourceSets.main <--- REPLACED WITH FOLLOWING LINES!!!
sourceDirectories = it.sourceSets.main.java
classDirectories = it.sourceSets.main.output.classesDirs
reports {
xml.enabled true
html.enabled true
}
}
So I changed this:
sourceSets it.sourceSets.main
to this:
sourceDirectories = it.sourceSets.main.java
classDirectories = it.sourceSets.main.output.classesDirs
The difference here that we explicitly state: sourceSets.main.output.classesDirs
which excludes resources.
Upvotes: 3