Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sonar branch coverage on class declaration

I have a class in Sonar:

public class Foo {
..... much code ....
}

And Sonar is reporting 1/2 branches covered on that public class Foo line. What does this mean? How do you test a line which declares a class?

Edit: in case it matters, this is Sonar v3.5.

Edit 2: A screenshot showing what I mean, note the 1/2 beside the "public class" on line 9. When hovering above this I get a tooltip stating "1 branches are covered by tests"

http://img829.imageshack.us/img829/2626/screenshot20130607at120.png

Edit #3: Ok, upon a bit more investigation, I've narrowed it down to smallest snippet I can find that triggers this:

public class Foo {

    Foo(final String s) {
        assert (s != null);
    }
}

If that assert doesn't exist in the constructor, the "N/2 branches covered" flag doesn't get generated in Sonar. If the assert is gone, then the flag goes away as well. So my guess is that it's based upon the branches within the constructor? (this code has 0/4 branches covered for the assert line, and 0/2 for the public class line).

like image 307
Adam Parkin Avatar asked Jun 07 '13 15:06

Adam Parkin


People also ask

How does SonarQube show coverage?

SonarQube itself does not calculate coverage. To include coverage results in your analysis, you must set up a third-party coverage tool and configure SonarQube to import the results produced by that tool. Below, you'll find guidelines and resources, as well as language- and tool-specific analysis parameters.

How do I set code coverage in SonarQube?

To add coverage to your Maven project you need to use the jacoco-maven-plugin and its report goal to create a code coverage report. Typically, you would create a specific Maven profile for executing the unit tests with instrumentation and producing the coverage report only on demand.

How do I improve sonar code coverage?

to increase your code coverage, the suggestion is to write tests. It can be unit tests (easiest way) or other tests (Integration test, System tests) which may contribute to coverage when a tool can report coverage for these.

How do I know my JaCoCo branch coverage?

Branches (C1 Coverage)JaCoCo also calculates branch coverage for all if and switch statements. This metric counts the total number of such branches in a method and determines the number of executed or missed branches. Branch coverage is always available, even in absence of debug information in the class files.


1 Answers

It looks like this is an issue related to the JaCoCo code coverage component of Sonar. JaCoCo works on compiled bytecode rather than Java source, and the Java compiler can produce code which is not directly related to the underlying source.

Looking at the docs for JaCoCo, there's a section which reads (emphasis added):

In some situations it is not obvious, why particular lines do have highlighting or have a particular color. The reason is that the underlying code coverage library JaCoCo works on Java class files only. In some cases the Java compiler creates extra byte code for a particular line of source code. Such situations might be filtered by future versions of JaCoCo/EclEmma.

Following the link in the passage takes you to the FilteringOptions page on the Jacoco's GH site, and it mentions a number of ways in which the JDK can potentially produce code which will will trigger these "spurious" code coverage warnings.

However, that's not what's at play here (or not exactly).

As mentioned JaCoCo works on Java bytecode, so any code which is produced by the compiler that isn't directly attributed to the source will count towards coverage.

In my specific case, I had an assert which, in the source, represents a branch at the point where the assert happens, but also at a "global" level. If you look at the bytecode for the Foo class defined above (do a javap -c Foo), you'll see:

Compiled from "Foo.java"
public class Foo extends java.lang.Object{
static final boolean $assertionsDisabled;

Foo(java.lang.String);
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   getstatic   #2; //Field $assertionsDisabled:Z
   7:   ifne    22
   10:  aload_1
   11:  ifnonnull   22
   14:  new #3; //class java/lang/AssertionError
   17:  dup
   18:  invokespecial   #4; //Method java/lang/AssertionError."<init>":()V
   21:  athrow
   22:  return

static {};
  Code:
   0:   ldc_w   #5; //class Foo
   3:   invokevirtual   #6; //Method java/lang/Class.desiredAssertionStatus:()Z
   6:   ifne    13
   9:   iconst_1
   10:  goto    14
   13:  iconst_0
   14:  putstatic   #2; //Field $assertionsDisabled:Z
   17:  return

Note line 7, which is a conditional branch dependent upon whether or not assertions are enabled. Thus if you have a class with a plain Java assert in it, you'll have this branch somewhere in the bytecode, and this is what produces the "N/2 branches executed" coverage warning on the class declaration, where N is either 0 or 1 depending upon if the class was ever exercised by a test (1) or not (0).

Edit: note that this is also mentioned in https://sourceforge.net/apps/trac/eclemma/wiki/FilteringOptions:

Blocks that throw AssertionErrors - Entire block should be ignored if a condition (if !assertion throw new AssertionError)

like image 109
Adam Parkin Avatar answered Sep 18 '22 01:09

Adam Parkin