jansauer / gradle-print-coverage-plugin

Scraps jacoco test reports and prints the code coverage to the console
16 stars 9 forks source link

PrintCoverage with multi-module gradle project #1

Open yurii-polishchuk opened 6 years ago

yurii-polishchuk commented 6 years ago

Hi @jansauer,

Nice and lightweight plugin for Jacoco coverage console output. Appreciate your work.

I've tried your plugin in my pet multi-module Gradle project, but it looks like I've missed something. Does plugin support it? I didn't have a chance to look at your code, but when I run printCoverage on a root level, I have error, though running command per individual subproject works well.

gradlew check printCoverage
* What went wrong:
Execution failed for task ':printCoverage'.
> empty String

settings.gradle:

rootProject.name = "root"
include("sub1", "sub2", "sub3")

build gradle root:

buildscript {
    repositories {
        jcenter()
        gradlePluginPortal()
    }
    dependencies {
        classpath "gradle.plugin.de.jansauer.printcoverage:printcoverage:2.0.0"
    }
}

allprojects {
    apply plugin: "de.jansauer.printcoverage"
...
}
...

I would appreciate feedback from you!

Thanks, Yurii

yurii-polishchuk commented 6 years ago

Ou, It looks like that your print functionality depends on XML report, hmmm. Might worth to extend implementation for printcoverage() and catch subprojects reports with some math/aggregation involved for coverage result.

jansauer commented 6 years ago

Hi @yurii-polishchuk Thx for your feedback. I have not yet worked with multi-module but should be interesting to get this working :-D Yes 'jacoco' generates the xml report and my plugin processes it. I'm at work right now but hopefully I will have time this evening to reproduce is and extend the plugin for multi-module support.

yurii-polishchuk commented 6 years ago

Cool Thnx,

For now, I did a trick and just merged all the results to root project level. overall looks good.

build.gradle (root one)

ext {
    junitJupiterVersion = "5.2.0"
    jacksonVersion = "2.9.5"
    jacocoTestReportXml = "${buildDir}/reports/jacoco/test/jacocoTestReport.xml"
    jacocoTestPattern = "**/build/jacoco/*.exec"
}

buildscript {
    repositories {
        jcenter()
        gradlePluginPortal()
    }
    dependencies {
        classpath "gradle.plugin.de.jansauer.printcoverage:printcoverage:2.0.0"
    }
}

allprojects {
    apply plugin: 'java'
    apply plugin: 'idea'
    apply plugin: 'jacoco'
    apply plugin: "de.jansauer.printcoverage"

    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8

    repositories {
        jcenter()
    }

    dependencies {
        testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}"

        testImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}"
        testImplementation "org.junit.jupiter:junit-jupiter-params:${junitJupiterVersion}"
        testImplementation "org.mockito:mockito-junit-jupiter:2.18.3"
    }

    test {
        useJUnitPlatform()
    }

    jacoco {
        toolVersion = '0.8.1'
    }

    check.dependsOn jacocoTestCoverageVerification
}

task codeCoverageReportRoot(type: JacocoReport) {

    // Gather execution data from all sub-projects
    // (change this if you e.g. want to calculate unit test/integration test coverage separately)
    executionData fileTree(project.rootDir.absolutePath).include(jacocoTestPattern)

    // Add all relevant source-sets from the sub-projects
    subprojects.each {
        sourceSets it.sourceSets.main
    }

    // Generate global merged report
    reports {
        xml.setEnabled(true)
        xml.setDestination(file(jacocoTestReportXml))
        html.setEnabled(false)
        csv.setEnabled(false)
    }
}

// Always run the tests before generating the report
codeCoverageReportRoot.dependsOn subprojects.collect { it.tasks.check }
codeCoverageReportRoot.dependsOn jacocoTestReport
printCoverage.dependsOn codeCoverageReportRoot

project(":sub1")
project(":sub3") {
    dependencies {
        implementation project(":sub1")
    }
}
project(":sub2") {
    dependencies {
        implementation project(":sub1")
    }
}

As a result:

image

As a result in console:

> Task :printCoverage
Coverage: 74.93%

> Task :sub1:jacocoTestReport UP-TO-DATE

> Task :sub1:printCoverage
Coverage: 75.58%

> Task :sub2:jacocoTestReport UP-TO-DATE

> Task :sub2:printCoverage
Coverage: 1.64%

> Task :sub3:jacocoTestReport UP-TO-DATE

> Task :sub3:printCoverage
Coverage: 95.02%
yurii-polishchuk commented 6 years ago

@jansauer though, it would be very helpfull, if in: https://github.com/jansauer/gradle-print-coverage-plugin/blob/master/src/main/groovy/de/jansauer/printcoverage/PrintCoverageTask.groovy

File jacocoTestReport = new File("${project.buildDir}/reports/jacoco/test/jacocoTestReport.xml") Would not be hardcoded

it would be much easier to make it configurable and tell your task to parse any XML

amimas commented 6 years ago

I agree with @yurii-polishchuk . It would be very helpful if we could configure where/which xml report to parse.

I maintain a lot of projects and almost all of them are Multi module Gradle projects. I needed an overall coverage report. So, I also ended up doing the same thing. In my root project's build.gradle file, I added a task that creates an aggregated report of all the subprojects.

task combinedCoverageReport(type: JacocoReport) {
    additionalSourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs)
    sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs)
    classDirectories =  files(subprojects.sourceSets.main.output)
    executionData = files(subprojects.jacocoTestReport.executionData)
    reports {
        html.enabled = true
        xml.enabled = true
        csv.enabled = false
    }

    doFirst {
        classDirectories = files(classDirectories.files.collect { 
            fileTree(dir: it, excludes: [
                '**/some/package/to/be/excluded/if/needed/*'
            ]) 
        })
        executionData = files(executionData.findAll { it.exists() })
    }
}

The above task will generate the combined xml report under $rootProject.buildDir/jacoco/combinedCoverageReport/combinedCoverageReport.xml

I then added another Copy task that will copy the above xml report generated by above task to $rootProject.buildDir/jacoco/test/jacocoTestReport.xml, which is what this plugin expects.

task copyRootCoverageReport(type: Copy) {
    from (combinedCoverageReport) {
        include "combinedCoverageReport.xml"
    }
    rename ("combinedCoverageReport.xml", "jacocoTestReport.xml")
    into "$buildDir/reports/jacoco/test"
}
pdegand commented 4 years ago

@amimas Your message is from a long time ago but ... You can add

report {
  ...
  xml.destination = file("$buildDir/reports/jacoco/test/jacocoTestReport.xml")
  ...
}

in your combinedCoverageReport instead of having a separate copy task.