SonarQube not reporting JaCoCo coverage

Must-share information (formatted with Markdown):

  • which versions are you using (SonarQube, Scanner, Plugin, and any relevant extension)
    Version 9.5 (build 56709)

  • what are you trying to achieve
    Building app via Azure Pipelines with SonarQube 3.*, when jacoco code coverage is created it is viewable via the ‘code coverage’ section in the builds dashboard but does not show in projects sonarqube dahsboard. Reporting 0.0% after upgrading jacoco plugin from 0.8.5 to 0.8.7

  • what have you tried so far to achieve this
    Followed all sonarqube documentation, followed community guides on similar issues but still unable to view code coverage within SQ

After running debug on our gradle task the output shows the coverage file being consumbed by the SQ plugin

[INFO] [org.sonarqube.gradle.SonarQubeTask]  Sensor JaCoCo XML Report Importer [jacoco]
[INFO] [org.sonarqube.gradle.SonarQubeTask]  Importing 1 report(s). Turn your logs in debug mode in order to see the exhaustive list.
[DEBUG] [org.sonarqube.gradle.SonarQubeTask] Reading report '/home/vsts/work/1/s/mobile/build/reports/jacoco/jacoco***Coverage/jacoco***Coverage.xml'
[INFO] [org.sonarqube.gradle.SonarQubeTask]  Sensor JaCoCo XML Report Importer [jacoco] (done) | time=162ms

Running trace logs on SQ itself does not show any additional information or errors

We have 2 Android projects, 1 of which reports coverage ok and the second is the one having the issue.

Here are the two jacoco.gradle files

Works:

ProjA

apply plugin: 'jacoco'

android {
    buildTypes {
        debug {
            testCoverageEnabled = false
        }
    }
}

jacoco {
    toolVersion = "0.8.7"
}

tasks.withType(Test) {
    jacoco.includeNoLocationClasses = true
}

project.afterEvaluate {
    def buildTypes = android.buildTypes.collect { type ->
        type.name
    }
    def product = android.product.collect { flavor ->
        flavor.name
    }
    if (!product) product.add('')
    productFlavors.each { product ->
        buildTypes.each { buildTypeName ->
            def buildVariant
            if (!product) {
                buildVariant = "${buildTypeName}"
            } else {
                buildVariant = "${product}${buildTypeName.capitalize()}"
            }
            def testTaskName = "test${buildVariant.capitalize()}UnitTest"
            // Create coverage task of form 'jacocoCverage' depending on 'testUnitTest'
            task "jacoco${buildVariant.capitalize()}Coverage"(type: JacocoReport, dependsOn: "$testTaskName") {
                group = "Reporting"
                description = "Generate Jacoco coverage reports"

                def fileFilter = [
                        '**/R.class',
                        '**/R$*.class',
                        '**/BuildConfig.*',
                        '**/Manifest*.*',
                        '**/*App.*',
                        '**/*Activity.*',
                        '**/*Fragment.*',
                        '**/*Service.*',
                        '**/*Receiver.*',
                        '**/*Adapter.*',
                        '**/*ViewHolder.*',
                        '**/*Broadcast.*',
                        '**/*Dialog.*',
                ]

                // The path for java compiled classes
                def javaDebugTree = fileTree(
                        dir: "${buildDir}/intermediates/javac/${buildVariant}/classes",
                        excludes: fileFilter)
                // The path for kotlin compiled classes
                def kotlinDebugTree = fileTree(
                        dir: "${buildDir}/tmp/kotlin-classes/${buildVariant}",
                        excludes: fileFilter)

                classDirectories.from = files([javaDebugTree + kotlinDebugTree])
                def coverageSourceDirs = [
                        "src/main/java",
                        "src/$pName/java",
                        "src/$bTypeName/java"
                ]
                additionalSourceDirs.from = files(coverageSourceDirs)
                sourceDirectories.from = files(coverageSourceDirs)
                executionData.from = files("${project.buildDir}/jacoco/${testTaskName}.exec")
                reports {
                    html.enabled = true
                    xml.enabled = true
                }
            }
        }
    }
}

Not working:

ProjB

apply plugin: 'jacoco'

android {
    buildTypes {
        debug {
            testCoverageEnabled = false
        }
    }
}

jacoco {
    toolVersion = "0.8.7"
}

tasks.withType(Test) { task ->
    jacoco.includeNoLocationClasses = true

    // see related issue https://github.com/gradle/gradle/issues/5184#issuecomment-457865951
    jacoco.excludes = ["jdk.internal.*"]
}

project.afterEvaluate {
    def testableVariants = android.unitTestVariants

    if (!testableVariants) testableVariants.add('')

    testableVariants.each { testableVariant ->

        def buildTypeName = testableVariant.buildType.name
        def bName = testableVariant.productF[0].name
        def cName = testableVariant.productF[1].name
        def testTaskName = "test${testableVariant.name.capitalize()}"

        task "jacoco${bName.capitalize()}${cName.capitalize()}${buildTypeName.capitalize()}Coverage"(type: JacocoReport, dependsOn: "$testTaskName") {
            group = "Reporting"
            description = "Generate Jacoco coverage reports"

            // list of files to omit from unit test code coverage reports
            def fileFilter = [
                    // Generated classes
                    '**/R.class',
                    '**/R$*.class',
                    '**/BuildConfig.*',
                    '**/Manifest*.*',
                    '**/*_Impl*',
                    '**/*App.*',
                    '**/*Activity.*',
                    '**/*Fragment.*',
                    '**/*Service.*',
                    '**/*Receiver.*',
                    '**/*Adapter.*',
                    '**/*ViewHolder.*',
                    '**/*Broadcast.*',
                    '**/*Dialog.*'
            ]

            // The path for java compiled classes
            def javaDebugTree = fileTree(
                    dir: "${buildDir}/intermediates/javac/${testableVariant.name}/classes",
                    excludes: fileFilter)
            // The path for kotlin compiled classes
            def kotlinDebugTree = fileTree(
                    dir: "${buildDir}/tmp/kotlin-classes/${testableVariant.name}",
                    excludes: fileFilter)

            classDirectories.from = files([javaDebugTree + kotlinDebugTree])
            def coverageSourceDirs = [
                    "src/main/java",
                    "src/$buildTypeName/java",
                    "src/$bName/java",
                    "src/$cName/java"
            ]
            additionalSourceDirs.from = files(coverageSourceDirs)
            sourceDirectories.from = files(coverageSourceDirs)
            executionData.from = files("${project.buildDir}/jacoco/${testTaskName}.exec")
            reports {
                html.enabled = true
                xml.enabled = true
            }
        }

    }
}

I have included the exclude filter items into each of the projects SQ config ‘sonar.coverage.exclusions’ also ‘sonar.exclusions’ both together and separately without success.

The project with the issue was working ok until we upgraded the Jacoco plugin from 0.8.5 to 0.8.7.

Hey there.

SonarQube doesn’t generate JaCoCo reports – it only reads them. After upgrading your JaCoCo version – does the raw XML reports indicate that coverage is being reported correctly?

If the issue is with the report itself, you should raise an issue with Issues · jacoco/jacoco · GitHub (take note that the latest version is 0.8.8, not 0.8.7)

Hi Colin,

Yes the coverage xml is created ok. This is published ok to the azure pipeline build and reporting the correct converge.

SonarQube also reads the report file at the specified path defined here ‘sonar.coverage.jacoco.xmlReportPaths=’ but the results are not shown in the SQ project dash

The two xml codes i provided is our jacoco config that generates the reports, they both generate the coverage xml ok but only projA is shown in SQ (2 separate SQ projects)

projB did report ok but after upgrading the jacoco plugin from 0.8.5 to 0.8.7 it stopped showing in SQ
projA one is still reporting ok after the upgrade

For further context we upgraded:

                    Kotlin 1.4.32 -> Kotlin 1.6.21
Android Gradle Build Tools: 4.1.1 -> Android Gradle Build Tools 4.1.1

We also tried using the latest jacoco plugin 0.8.8 but the same issue.

Report is generated and SQ is able to get the report but looks like it is unable to parse the report. We can’t get any other debug info out to check the actual issue.

And if you downgrade to 0.8.6 of the JaCoCo plugin (with no other changes), everything starts working again?

We tried 0.8.6 with the same issue, only 0.8.5 works but then this isn’t compatible with our upgrade to kotlin_version 1.6.21

I would really recommend performing a simple diff on the reports generated by 0.8.5 and 0.8.7 and see if you can spot differences that would cause an issue in the coverage being reported. You’re also welcome to upload the reports here.