Gradle code coverage report

Greetings all,

I’ve just hacked together a little gradle task to modify the standard jacoco coverage output to make it a bit more Kotlin friendly.

The default output navigates by class which isn’t very friendly as Kotlin generates numerous anonymous and function classes.

This hack provides a file-based view instead by merging the jacoco source index files into a combined index.

Anyway, I thought it might be useful to others.

To use, add the following to your gradle project:

apply plugin: 'jacoco'
 
 
 
task coverage(dependsOn: 'jacocoTestReport') << {
    def reports = new File(project.buildDir, '/reports/jacoco/test/html')
    def index = new File(reports, 'index.html').getText('UTF-8')
    def footer = '<div class="footer">'
    def result = new StringBuilder(index.substring(0, index.indexOf(footer)))
 
    reports.eachDirMatch({ !new File(it).name.startsWith(".") }) { dir ->
        def moduleIndex = new File(dir, 'index.source.html')
        def content = moduleIndex.getText('UTF-8')
                .replaceAll('\\.\\./\\.resources', '.resources')
                .replaceAll('href="', "href=\"${dir.name}/")
        result.append(content.substring(content.indexOf('<h1>'), content.indexOf(footer)))
    }
    result.append(index.substring(index.indexOf(footer)))
 
    def output = new File(reports, 'index.source.html')
 
    output.delete()
    output << result.toString()
    println("open $output.path")
}
 

This will generate a combined index for your module. e.g.

You can then directly navigate to the Kotlin source:


Cheers,
Andrew

6 Likes

With my current version of jacoco, the resources dir is not .resources but jacoco-resources.

So just a little fix :

task coverage {
    inputs.dir("$buildDir/reports/jacoco/test/html")
    outputs.file("$buildDir/reports/jacoco/test/html/index.source.html")
    doLast {
        def reports = new File(project.buildDir, '/reports/jacoco/test/html')
        def index = new File(reports, 'index.html').getText('UTF-8')
        def footer = '<div class="footer">'
        def result = new StringBuilder(index.substring(0, index.indexOf(footer)))

        reports.eachDirMatch({
            !it.startsWith(".") && it != "jacoco-resources"
        }) { dir ->
            def moduleIndex = new File(dir, 'index.source.html')
            def content = moduleIndex.getText('UTF-8')
                    .replaceAll('\\.\\./\\.resources', '.resources')
                    .replaceAll('\\.\\./jacoco-resources', 'jacoco-resources')
                    .replaceAll('href="', "href=\"${dir.name}/")
            result.append(content.substring(content.indexOf('<h1>'), content.indexOf(footer)))
        }
        result.append(index.substring(index.indexOf(footer)))

        def output = new File(reports, 'index.source.html')

        output.delete()
        output << result.toString()
    }
}