Search code examples
gradlebuild.gradlejacocojacoco-plugin

jacocoRootReport only shows coverage from last project of multi-project gradle build


I'm upgrading my Gradle 4 multi-project setup to Gradle 6. I've followed instructions here:

https://stackoverflow.com/a/56181389

and I've bumped the jacoco version to 0.8.5. Right now the only problem is that the human-readable coverage report seems to be missing most of the coverage data that it was showing under the old version. It seems that the coverage report is only reflecting the last (most recently tested) project. It used to work fine under Gradle 4. I'm using Java 8.

I ran the gradle build using --debug, and I notice that the test.exec file is deleted repeatedly, once for each subproject that has tests. I think this is the problem, but I don't know how to prevent deletion of this file.

2020-04-16T09:16:21.048-0600 [DEBUG] [org.gradle.internal.file.impl.DefaultDeleter] Deleting /Users/bishop/dev/aep/edge-profile-lookup-target/build/jacoco/test.exec

Can someone please help me fix this so that all of the coverage (from each of the tests which ran against each of the sub projects) appear in a single coverage report?

Here are the parts of the main build.gradle file that seem relevant:

buildscript {
    ext {
        jacocoVersion = '0.8.5'
        ...
    }
    ...
}

allprojects {
    ...
    apply plugin: 'jacoco'
    ...
}

subprojects {
    tasks.withType(Test) {
        // redirect all coverage data to one file
        jacoco {
            destinationFile = file("$rootProject.buildDir/jacoco/test.exec")
        }
    }
    jacoco {
        toolVersion = jacocoVersion
    }

    jacocoTestReport {
        additionalSourceDirs.from = files(sourceSets.main.allSource.srcDirs)
        sourceDirectories.from = files(sourceSets.main.allSource.srcDirs)
        classDirectories.from = files(sourceSets.main.output.collect {
            fileTree(dir: it, exclude: project.properties['BUILD_COVERAGE_EXCLUSIONS'].tokenize(','))
        })
        reports {
            html.enabled = true
            xml.enabled = true
            csv.enabled = false
        }
    }
}

task jacocoRootReport(type: JacocoReport) {
    dependsOn = subprojects.test
    additionalSourceDirs.from = files(subprojects.sourceSets.main.allSource.srcDirs)
    sourceDirectories.from = files(subprojects.sourceSets.main.allSource.srcDirs)
    classDirectories.from = files(subprojects.jacocoTestReport.classDirectories)
    executionData.from = files(subprojects.jacocoTestReport.executionData)
    reports {
        html.enabled = true
        xml.enabled = true
        csv.enabled = false
    }
    onlyIf = {
        true
    }
    doFirst {
        executionData.from = files(executionData.findAll {
            it.exists()
        })
    }
}

...
apply plugin: 'jacoco'

configurations.create("jacoco")

configurations {
    jacoco
}

dependencies {
    jacocoAnt group: 'org.jacoco', name: 'org.jacoco.ant', version: jacocoVersion
    jacocoAnt group: 'org.ow2.asm', name: 'asm', version: asmVersion
}

task copyRuntimeLibs(type: Copy) {
    from configurations.jacoco
    into "$rootProject.buildDir/libs"
}

build.dependsOn copyRuntimeLibs

Solution

  • The following:

    jacoco {
        destinationFile = file("$rootProject.buildDir/jacoco/test.exec")
    }
    

    configures Jacoco to always use the same output file.

    So the issue is most likely that more recent Gradle versions work on separating colliding outputs.

    I would recommend looking at the recently published sample on how to setup Jacoco in a multi project instead of attempting to rely on colliding outputs.