Exclude methods from code coverage with Cobertura - java

Is there a way to exclude code from inclusion into Cobertura coverage reports? We have some methods that should not be included in the coverage report and therefore not drive down the coverage numbers.
I know that Clover has such a functionality, but I have not found anything similar for Cobertura.

You can exclude classes from instrumentation. Then they should not appear on reports. See exclude statements below.
You can also ignore calls to some methods. See ignore statement below.
If you are using maven, see maven plugin manual.
<configuration>
<instrumentation>
<ignores>
<ignore>com.example.boringcode.*</ignore>
</ignores>
<excludes>
<exclude>com/example/dullcode/**/*.class</exclude>
<exclude>com/example/**/*Test.class</exclude>
</excludes>
</instrumentation>
</configuration>
And for ant see this.
<cobertura-instrument todir="${instrumented.dir}">
<ignore regex="org.apache.log4j.*" />
<fileset dir="${classes.dir}">
<include name="**/*.class" />
<exclude name="**/*Test.class" />
</fileset>
<fileset dir="${jars.dir}">
<include name="my-simple-plugin.jar" />
</fileset>
</cobertura-instrument>

This has been breaking my head for some time now.
My problem was that I had the cobertura maven plugin setup in the reporting section instead of the build section.
The instrumentation settings, and hence the excluding of classes or packages, won't be applied if you don't set it up on build section, so watch out for this.

Remember to exclude inner classes too.
<exclude>path/to/class/MyClass*.class</exclude>
It took me ages to notice I was missing an asterisk!

Cobertura doesn't currently provide such a feature, and neither does Emma (which we use) although it is listed as a forthcoming enhancement - although in the form of an extension to the exclusion rules I believe rather than as an annotation.
Would be handy to cover off those few inaccessible corners cleanly so that you can strive for 100% without being ridiculous.
I think annotations would probably be a friendlier way to do it, but they ought to be fairly explicitly named and based on a list of acceptable scenarios as I fear otherwise something like '#ExcludeFromCoverage' would get added over generously.

Since 2.0 you can write your own #CoverageIgnore annotation.
It will be recognized by Cobertura, which will avoid considering annotated methods (does not work on classes as far as I know).
Create an empty annotation:
public #interface CoverageIgnore {}
Then annotate the methods you want to exclude from the reports:
public class SomeClass {
#CoverageIgnore
public void foo(String baz) {
// useless stuff
}
}
Source: https://github.com/cobertura/cobertura/wiki/Coverage-Annotations

Related

Using classes from /target

I am using JOOQ. JOOQ generates meta classes, using with database requests. It's recommended to generate classes into /target/generated-sources/jooq. But when I try to call these meta classes in code, they are not accessible.
Please, tell me what to do.
This is the default output location because the assumption is that most people are using Maven, and Maven will automatically include that path.
You can generate your classes anywhere you want including your own src directory, or whatever your IDE is defaulting to. For that, use the target configuration. Example from the manual:
<configuration xmlns="http://www.jooq.org/xsd/jooq-codegen-3.14.0.xsd">
<generator>
<target>
<packageName>org.jooq.your.packagename</packageName>
<directory>/path/to/your/dir</directory>
</target>
</generator>
</configuration>

JaCoCo and MR Jars

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?
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.
Source
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.
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.

manage maven different cglib/asm versions

I have a multi-module project in maven, that uses (amongst others) glassfish-jersey, jersey-moxy, wicket-ioc, lucene and lamdbaj These all come with asm, but all with different versions.
Lately, I run into a lot of trouble when running my tests. Typical error I get is:
java.lang.VerifyError: class net.sf.cglib.core.DebuggingClassWriter overrides final method visit.(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V
I read that this can be caused by different asm versions. Is there a way to 'sandbox' these different asm-versions in their dependencies, so they don't get mixed up?
Edit:
My current solution is to use jarjar, like this:
<build>
<plugins>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>jarjar-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jarjar</goal>
</goals>
<configuration>
<includes>
<include>cglib:cglib-nodep</include>
</includes>
<rules>
<rule>
<pattern>net.sf.cglib.asm.**</pattern>
<result>com.myproject.lambda4j.asm.#1</result>
</rule>
<rule>
<pattern>net.sf.cglib.**</pattern>
<result>com.myproject.lambda4j.cglib.#1</result>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
Try explicitly adding some cglib v3.* on your class path which uses a recent version of ASM. The problem that you encounter is that cglib modifies the behavior of the ASM class writer by inheritance rather then by delegation. However, ASM enforced this latter best practice by making its ClassWriter's methods final from any version 4.* while it was still possible to override its methods in version 3. The error you encounter is a result of combining cglib 2.* with ASM 4.*.
Fortunately (for you), cglib has been rather static in its last versions, i.e. there were only minor API changes while the newer versions mostly consisted of updates of ASM. If you are lucky, this explicit use of cglib v3.* therefore solves your problem. This holds as long as none of your project dependencies has a direct dependency on ASM what seems reasonable for the dependencies you named like Jersey or Lucene.
If this does not work, you need to recompile some of your dependencies while using a tool like jarjar in order to repack the direct ASM dependencies into different name spaces in order to resolve these version conflicts. An alternative could be to isolate different ASM versions by some conditional child-first ClassLoader magic but this is not so recommendable since the effects are unpredictable and will also result in a performance penalty.

Cobertura Instrumentation

I am trying to get Cobertura working with my Ant build, and specifically just want it to give me a coverage report on my unit tests. I'm using the following directory structure:
src/main/java --> main source root
src/test/java --> test source root
bin/main --> where main source compiles to
bin/test --> where test source compiles to
gen/cobertura --> cobertura root
gen/cobertura/instrumented --> where "instrumented" class will be copied to
My understanding of Cobertura (and please correct me if I'm wrong!!) is that it adds bytecode to compiled classes (aka "instrumentation") and then runs reports based on that injected/woven bytecode.
So my question is, if Cobertura changes the bytecode of the classes its instrumenting, should I run JUnit on my test sources before <cobertura:instrument>, or after, and why?
You're correct that Cobertura instruments the byte code of your compiled classes. You normally want to exclude your test sources from coverage analysis, since the test classes are effectively the drivers that generate the coverage. The basic example build.xml provided with Cobertura gives a good example when it calls cobertura-instrument:
<cobertura-instrument todir="${instrumented.dir}">
<!--
The following line causes instrument to ignore any
source line containing a reference to log4j, for the
purposes of coverage reporting.
-->
<ignore regex="org.apache.log4j.*" />
<fileset dir="${classes.dir}">
<!--
Instrument all the application classes, but
don't instrument the test classes.
-->
<include name="**/*.class" />
<exclude name="**/*Test.class" />
</fileset>
</cobertura-instrument>
</target>
The exclude element here excludes all the classes with "Test" in their names from being instrumented.
Here's a working example of how the Cobertura ANT tasks are used in conjunction with Junit to generate a code coverage report
SONAR - Measure Code Coverage using Cobertura

How can I remove/filter/ignore some package from Emma (code coverage)

I`m trying to remove some package from my report and having trouble.
Could some one give me some help?
I'm using EMMA in my ant process.
<!-- Generate the emma report both in xml and html -->
<emma>
<report
sourcepath="${build.report.src}"
metrics="class:${coverage.classes.min},method:${coverage.methods.min}">
<fileset dir="${build.report.junit.data.dir}">
<include name="*.emma"/>
</fileset>
<html outfile="${build.report.reports}/emma/raw.html" depth="method"/>
<xml outfile="${build.report.tmp}/emma.xml" depth="method"/>
</report>
</emma>
I`ve tried to use:
<filter excludes="com.my.package.*"/>
But with no success :(
Emma allows the use of filters at instrumentation phase to specify a set of files that need to be instrumented. In contrast you are trying to do this at report generation phase. The link given above describes how to define the instrumentation set.
I've used filters like this:
<property name="emma.filter" value="-*.unittest.* -*.unittests.* -*.TST* -*TestCase -*Test -*TestSuite" />
<emma>
<instr instrpath="${build.dir}"
mode="overwrite"
metadatafile="${build.dir}/coverage.em"
filter="${emma.filter}" />
</emma>
You can also use nested <filter> elements under <instr>
I assume you've tried all of the variations in the documentation...?
If so, I expect that you may have a typo, or something like that. Can you provide the code you're using with the exclude syntax that doesn't work, and the header of the source file for the class that is incorrectly being included?

Categories