JaCoCo 0% coverage for objects created with Jackson.map.ObjectMapper - java

I have a Gradle project with the JaCoCo plugin applied to it. When I run my tests and create a jacocoTestReport I get this classes not matching error
[ant:jacocoReport] Classes in bundle 'e-services' do no match with execution data. For report generation the same class files must be used as at runtime.
[ant:jacocoReport] Execution data for class eservices/model/persistence/Event does not match.
The classes should match as I'm doing a clean -> build -> test locally. I suspect the mismatch comes from the fact that I'm using jackson.map.ObjectMapper to create an object from a JSON and somehow this causes the classId stored in jacoco's .exec file not match the compiled class id.
My test uses the Event class extensively and still I get 0% coverage due to class mismatch:
import eservices.model.persistence.Event;
event = mapper.readValue(json, Event.class);
event.setTenId(TenIds.getInternalId());
Is there a way to get coverage from this scenario?

This is a JaCoCo Known limitation as JaCoCo relies on the checksum of the runtime bytecodes matching the checksum of the bytecodes it uses for report generation. Typically it happens when you have two libraries instrumenting bytecodes, like PowerMock and JaCoCo, or JPA and JaCoCo.
This is referenced in the following JaCoCo issue Coverage is missing a class that was in fact tested #193 and it is labeled as 'wontfix' 'known limitation'

Related

Dagger can not find classes generated by other annotation processor

I have written a simple Annotation Processor (just for fun) that will generate some boilerplate code that I have been writing in my previous project. It actually generates a module like following by collecting annotations on Activity classes
#Module
abstract class ActivityInjectorModule {
#ContributesAndroidInjector
abstract fun providesMain2Activity(): Main2Activity
#ContributesAndroidInjector
abstract fun providesMainActivity(): MainActivity
}
However, when I run it with dagger, dagger can't seem to find classes generated by my annotation processor. Although, class is generated and present in generated directory, I can use it in my source code but on compilation, dagger produces the following exception. Any expert suggestion?
error: cannot find symbol
#dagger.Component(modules = {dagger.android.AndroidInjectionModule.class, com.mallaudin.daggietest.di.AppModule.class, ActivityInjectorModule.class})
^
symbol: class ActivityInjectorModule
This is the main app component.
#Singleton
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityInjectorModule::class
]
)
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
fun addContext(#BindsInstance ctx: Context): Builder
fun build(): AppComponent
}
}
ActivityInjectorModule class is generated by annotation processor and exists in the generated directory.
Application class
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().addContext(this).build()
}
}
Everything works perfectly, if I create the generated class myself.
Somehow on compile time, dagger is unable to find the class when generated by my annotation processor.
After Yuriy Kulikov's answer,
You can see generated file is in the same package but also referenced with fully qualified name. Still dagger reports errors.
Here is the link to github repository if someone wants to experiment
Solution:
Generate java code. Kapt does not support multiple rounds.
Write generated files on earliest possible round.
Explanation:
Javac annotation processor uses rounds instead of defining processors order. So normally the simplified algorithm is like that:
Gather all java sources
Run all annotation processors. Any annotation processor can generate new files using Filer.
Gather all generated files and if there are any, run step 2 again.
If there are no files generated, run one more round where RoundEnvironment.processingOver() returns true, signaling this is the last round.
Here is a pretty good explanation of the process
Now a bit about kapt. Kapt uses javac to run annotation processors. To make it possible, it runs kotlin compliler first to generate java stub files and runs javac on them. Currently kapt does not support multiple rounds, meaning it does not generate java stubs for kotlin classes, generated by annotation processors.
Note: javac still uses multiple rounds, it just can't pick up generated kotlin sources.
So, back to your question. One possible option is to move your generated classes into a separate module like it's described here.
But the easiest option is to generate java code directly and your generated java classes will be picked up by javac automatically, launching second round of annotation processing, where dagger will process them.
Just a few more notes:
Do not generate your code when RoundEnvironment.processingOver() == true, it will not trigger another round. Generate it during the same round you see the annotation.
To make the generated code visible to annotation processor, write it using Filer.
New answer
I have somehow missed that you are using kapt. Kapt can process your classes, even without full qualified name (which is remarkable) if you add this to your build.gradle:
kapt {
arguments {
arg("argumentIncremental", 'true')
}
correctErrorTypes = true
}
More info about this: https://kotlinlang.org/docs/reference/kapt.html#non-existent-type-correction
Previous answer can be useful is someone has the same issue with annotationProcessor (apt) in gradle.
Short answer: use fully qualified name for ActivityInjectorModule:
#dagger.Component(modules = {dagger.android.AndroidInjectionModule.class, com.mallaudin.daggietest.di.AppModule.class, com.mallaudin.daggietest.di.ActivityInjectorModule.class})
Alternatively put both files in the same package.
Long answer: Dagger is an annotation processor, it runs before your code is compiled and (potentially) before your other annotation processor runs. The sequence in which processors run is not defined.
Dagger annotation processor will process the TypeElement annotated with #dagger.Component and it will try to find all modules including the "ActivityInjectorModule.class". The thing is, ActivityInjectorModule might not have been generated yet. Therefore "ActivityInjectorModule" will not have a package at this point. Dagger will assume that ActivityInjectorModule resides in the same package as the Component class and will not add an import. The usual workaround for this is to use full-qualified names for generated classes, if they are used by other annotation processors. Sometimes it makes sense to move annotation processing to a difference gradle module, but I don't this that this is what you want.
There may be a more elegant way to solve this, but the simplest and most reliable solution is to do two passes with javac—once to run just your annotation processor, and the second to do everything it normally does.
The javac documentation specifies two options which should help you out.
-proc: {none,only}
Controls whether annotation processing and/or compilation is done. -proc:none means that compilation takes place without annotation processing. -proc:only means that only annotation processing is done, without any subsequent compilation.
-processor class1[,class2,class3...]
Names of the annotation processors to run. This bypasses the default discovery process.
The first pass (to run only your own annotation processor) is
javac -proc:only -processor com.foo.bar.MyProcessor MyProject/src/*
and the second pass (a regular build) is
javac MyProject/src/*
If you’re using something like Ant or Maven, you should be able to update the build instructions to have two compiler passes with only a minimal amount of effort.
Edit: here’s my attempt at Gradle instructions
I have no experience with Gradle, but it seems like you need to do something like this.
In your Gradle build script, you need to define the preprocessing task and add a dependency on your task to the javaCompile task.
javaCompile.dependsOn myAnnotationTask
task myAnnotationTask(type: JavaCompile) {
options.compilerArgs << '-proc:only' << '-processors com.foo.bar.MyAnnotationProcessor'
}

Does jacoco detect unused classes

Jacoco, as all other profiling capabilities in java I know of, depends on java agents/instrumentation.
Java instrumentation can only impact bytecode when a class is loaded.
I wonder if it is somehow possible to detect unused classes with Jacoco? Obviously, there might be classes that are "dead" and are in turn, never loaded by the ClassLoader.
When coverage output (HTML report, etc.) is generated after the test run has executed, JaCoCo scans additional classes in the runtime classpath that haven't been loaded during test execution, so they can also be instrumented and included in the output.
I implemented this same mechanism in my own code coverage tool (JMockit Coverage), which also relies on java.lang.instrument. It's the only way to have all relevant classes instrumented for coverage.
Yes, though it might be easier to specify -verbose:class if you are not already using Jacoco.
I just ran a test: I created a file called Delete.java that is never called by any other method. The generated Jacoco report shows the Delete class with 0% coverage.

SonarQube Multi-Module Code Coverage

I have a multi-module project that I can't seem to get an accurate unit test code coverage report on SonarQube. I use buildr and JaCoCo for the test coverage generation. The file heirarchy is similar to below.
Project
--Module1
----Reports
------Jacoco
--------jacoco.cov (jacoco execution file, previously used as .exec)
--Module2
--Reports
----Jacoco (Generated HTML, CSV, and XML report files)
----JUnit (JUnit xml report)
At this time, no unit tests exist for Module2. The problem I believe is that the overall coverage on SonarQube reflects only for Module1 and ignores Module2 completely. This makes the coverage appear higher than what it truly is for the whole project. Does anyone have any thoughts on this? Thank you for your input.
If I get your problem correctly you are looking to force coverage to 0% when there are no coverage file generated.
You have to set the correct parameter for this behaviour :
sonar.jacoco.reportMissing.force.zero=true
see documentation for more details : http://docs.sonarqube.org/display/PLUG/Usage+of+JaCoCo+with+Java+Plugin

Sonar test coverage does not include indirect classes

If I have the below classes (A and B) which are each in a separate module and I create a test for A.doSomething(), Sonar will complain about 0% coverage on B.doSomething() although it is being testing indirectly in TestA.
Using Ecclema coverage plugin for eclipse, I can see B.doSomething() is considered as covered.
Is there a reason for why Sonar acts this way? Is there a way to change the behavior of Sonar's code test coverage to include indirectly tested classes?
class A {
boolean doSomething() {
return new B().doSomething();
}
}
class B {
boolean doSomething() {
return true;
}
}
You can easily achieve what you want. This is a matter of how you configure JaCoCo so it has little to do with SonarQube.
For example in SonarQube Eclipse project we need that since unit tests are in a separate module.
So we have configured JaCoCo to use the same dump file for all modules (by default JaCoCo Maven plugin will use one separate dump per module):
https://github.com/SonarSource/sonar-eclipse/blob/master/pom.xml#L373
And then we configured SonarQube JaCoCo plugin to use the same report:
https://github.com/SonarSource/sonar-eclipse/blob/master/pom.xml#L357

Excluding Java test files from SonarQube 4.1.1 analysis

I do not wand violations to be reported for Java test files in SonarQube, but I do want JUnit and Cobertura code coverage to be analyzed and displayed (JUnit and code coverage reports are reused, not executed by SonarQube).
How do I exclude test files from violations analysis only? I tried adding to global exclusion these settings, but they are not working:
**/test/**
**/Test*.*
Thanks
SonarQube can ignore issues on certain components and against certain coding rules. You might want to read the Ignore Issues on Multiple Criteria section in SonarQube Narrowing the Focus.
As mentioned in the documentation:
You can ignore issues on certain components and for certain coding rules.
Examples:
I want to ignore all issues on all files => *;**/*
I want to ignore all issues on COBOL program bank/ZTR00021.cbl => *;bank/ZTR00021.cbl
I want to ignore all issues on classes located directly in the Java package com.foo, but not in its sub-packages => ;com/foo/
I want to ignore all issues against coding rule cpp.Union on files in the directory object and its sub-directories => cpp.Union;object/**/*

Categories