6

I have been looking for a solution for days for merging multiple Jacoco reports for a multi-module Android project in order to send them off to Sonarcloud at once. I have already checked tons of Stackoverflow posts and other things such as blogs, Github repositories, Gradle forum, etc but unfortunately none of solutions works for me.

I would really appreciate if anybody here shares a sample project or code piece with me.

Gradle version: 7.0.2
Kotlin version: 1.5.21
JDK: 11

Below code piece also doesn't work for me

/**
 *  Root task that generates an aggregated Jacoco test coverage report for all sub-projects
 */
task jacocoFullReport(type: JacocoReport, group: 'Coverage reports') {
    group = 'Reporting'
    description = 'Generates an aggregate report from all subprojects'

    tasks.withType(Test) {
        ignoreFailures true
    }

    def projects = subprojects

    //noinspection GrUnresolvedAccess
    dependsOn(projects.jacocoReport)

    final source = files(projects.jacocoReport.sourceDirectories)

    additionalSourceDirs.setFrom source
    sourceDirectories.setFrom source

    classDirectories.setFrom files(projects.jacocoReport.classDirectories)
    executionData.setFrom files(projects.jacocoReport.executionData)

    reports {
        html {
            enabled true
            destination file('build/reports/jacoco/html')
        }
        csv {
            enabled true
            destination file('build/reports/jacoco/jacocoFullReport.csv')
        }
    }

    doFirst {
        //noinspection GroovyAssignabilityCheck
        executionData.setFrom files(executionData.findAll { it.exists() })
    }
}
nuhkoca
  • 1,777
  • 4
  • 20
  • 44
  • The `org.sonarqube` Gradle plugin iterates through all sub projects and pushes all JaCoCo XML reports. Why do you need to merge them? – agabrys Oct 09 '21 at 16:53
  • @agabrys yes exactly but I just wanted to provide a single file instead. But you mean below one should be fine, right? `sonarqube { properties { property "sonar.coverage.jacoco.xmlReportPaths", fileTree(dir: "$projectDir", includes: ["**/reports/jacoco/*/jacocoTestReport.xml"]).files.join(", ") } } ` – nuhkoca Oct 09 '21 at 23:55
  • Remove the `sonar.coverage.jacoco.xmlReportPaths` property. Its default value does exactly what you need. Generally you shouldn't set any SonarScanner properties as long as you don't need to change the default behaviour. – agabrys Oct 10 '21 at 00:07
  • Unfortunately, unlike Kotlin modules, Android modules generates their reports under `**/jacocoTestReport/jacocoTestReport.xml`, but for Kotlin modules it is under `test` directory. – nuhkoca Oct 10 '21 at 10:24
  • 1
    By default JaCoCo Gradle plugin creates reports in the `build/reports/jacoco//jacocoTestReport.xml` file. SonarScanner by default configures `sonar.coverage.jacoco.xmlReportPaths` to checks `build/reports/jacoco/test/jacocoTestReport.xml`. Check if your `build.gradle` doesn't force creating Android JaCoCo reports in a different place. If yes - then remove it and use the defaults. If not - you may change to to use the standard location. – agabrys Oct 10 '21 at 12:38
  • 1
    Thanks, I forced my task to create reports for `Android` modules in the same location as other `Kotlin` modules and removed the property. Looks like it is working as like before. – nuhkoca Oct 10 '21 at 15:28
  • @agabrys one more question if still makes sense for you to ask here. I have a couple of modules that don't have tests at all. Therefore no need to apply both jacoco and sonarqube to gain time. But I apply sonarqube plugin to the entire project along with submodules so I am wondering about if it is a proper way to tell Sonarqube to skip such modules by adding `sonarqube { properties { isSkipProject = true } }` in respective `build.gradle`s or Sonar itself already don't visit those modules as they don't create any report? Thanks! By the way please add your answer so I can approve! – nuhkoca Oct 15 '21 at 21:19

2 Answers2

2

Here working example how to create aggregated jacoco report: https://github.com/SurpSG/jacoco-gradle-plugin-merge-coverage

task jacocoRootReport(type: JacocoReport, group: 'Coverage reports') {
    description = 'Generates an aggregate report from all subprojects'
    dependsOn(subprojects.test)

    additionalSourceDirs.from = files(subprojects.sourceSets.main.allSource.srcDirs)
    sourceDirectories.from = files(subprojects.sourceSets.main.allSource.srcDirs)
    classDirectories.from = files(subprojects.sourceSets.main.output)
    executionData.from = files(subprojects.jacocoTestReport.executionData)

    reports {
        html.enabled true
    }
}
SergiiGnatiuk
  • 522
  • 7
  • 13
  • 1
    thanks for the answer but this is not working for me. I am getting the error below: `Could not get unknown property 'test' for project ':app' of type org.gradle.api.Project.` – nuhkoca Oct 09 '21 at 18:08
  • 1
    If you get this error `Could not get unknown property 'test'` make sure to apply plugins to the sub projects `subprojects { apply plugin: 'java' apply plugin: 'jacoco' }` – Antoine Sauray Jan 24 '22 at 15:29
0

I just created this demo project illustrating how to archive this with a buildSrc ad-hoc plugin, that relies on Gradle's jacoco-report-aggregation and Android Gradle Plugin's native jacoco support.

Later you can easily link it with sonarqube by adding this to the root project:

plugins {
    coverage // comes from `buildSrc/src/main/kotlin/coverage.gradle.kts`
    id("org.sonarqube") version "3.4.0.2513"
}

sonarqube {
    properties {
        tasks.jacocoTestReport.configure {
            property("sonar.coverage.jacoco.xmlReportPaths", reports.xml.outputLocation.get().asFile)
        }
    }
}

tasks.sonarqube.configure {
    dependsOn(tasks.jacocoTestReport)
}
gmazzo
  • 1,135
  • 13
  • 15