bmuschko / gradle-clover-plugin

Gradle plugin for generating a code coverage report using Clover
Apache License 2.0
74 stars 50 forks source link
clover coverage gradle-plugin testing

Gradle Clover plugin Build Status

Clover Logo

Over the past couple of years this plugin has seen many releases. Thanks to everyone involved! Unfortunately, I don't have much time to contribute anymore. In practice this means far less activity, responsiveness on issues and new releases from my end.
I am actively looking for contributors willing to take on maintenance and implementation of the project. If you are interested and would love to see this plugin continue to thrive, shoot me a mail.

The plugin provides generation of code coverage reports using OpenClover.

Gradle Compatibility Tested

Built for OpenJDK JDK8 Tested with OpenJDK JDK8

Gradle Version Works
6.1 yes
6.9.4 yes
7.6.4 yes
8.10.1 yes

Gradle version 6.0.1 and older

Compatibility with Gradle 8.x required changes that make the plugin versions greater than 3.0.3 incompatible with Gradle 6.0.1 and older. Use 3.0.3 version or upgrade to a supported version of Gradle.

Gradle version 4.7 and older

For all versions of Gradle older than 4.8 use the 2.2.5 version of this plugin. Version 3.0.0 and newer only support Gradle 4.8 or later.

We are switching the build to run on JDK 11 which means we cannot test with Gradle 4.8 anymore, so I cannot guarantee that the 4.8 compatibility will be available after 3.0.2 release.

Usage

To use the Clover plugin, include in your build script:

apply plugin: 'com.bmuschko.clover'

The plugin JAR needs to be defined in the classpath of your build script. It is directly available on Gradle Plugins Portal. Alternatively, you can download it from GitHub and deploy it to your local repository. The following code snippet shows an example on how to retrieve it from Gradle Plugins Portal:

buildscript {
    repositories {
        gradlePluginPortal()
    }

    dependencies {
        classpath 'com.bmuschko:gradle-clover-plugin:3.0.7'
    }
}

To define the Clover dependency please use the clover configuration name in your dependencies closure. If you are working with a multi-module project make sure you apply the plugin and declare the clover dependency within the allprojects closure.

dependencies {
    clover 'org.openclover:clover:4.4.1'
}

With the introduction of the OpenClover support the licenseLocation clover convention is now unused. For compatibility with existing clover Gradle configurations the plugin will accept a value set but will do nothing with it.

GroovyDoc for API

The GroovyDoc for the plugin API can be found here: GroovyDoc

Tasks

The Clover plugin defines the following tasks:

When the cloverAggregateReports is created for a multi-project configuration the Clover report for the aggregated results is located in the $buildDir/reports/clover location. The cloverGenerateReport task for the root project only will use the $buildDir/reports/clover-root location in this case. This is done to avoid a problem when the root project has Clover enabled code in addition to subprojects with Clover enabled code. The problem was that cloverGenerateReport and the cloverAggregateReports task would overwrite their outputs and would be perpetually not UP-TO-DATE.

Convention properties

Within clover you can define coverage contexts in a closure named contexts. There are two types of coverage contexts: statement contexts and method contexts. You can define as many contexts as you want. Each of the context type closure define the following attributes:

Additionally, method contexts can be provided any of the following attributes to further configure how Clover should apply method coverage filters:

For more information on method contexts, see the main Clover documentation for this feature.

Within clover you can define which report types should be generated in a closure named report:

Within report closure you can define a closure named columns to enable selection of columns for the report output. This feature implements support for the columns defined in Clover documentation Clover ReportComumns Nested Element. Each line in the closure must begin with the name of the column to add followed by a Groovy map with the 4 optional attributes for the column. We support format, min, max and scope. The format and scope values are checked against the documented supported contents and will throw errors if unsupported values are used. We do not implement support for the expression column at this time, if you attempt to use it the plugin will throw an error:

Within report closure you can define a closure named historical to enable Clover historical reports generation:

Furthermore, within clover you can define compiler settings which will be passed to java and groovyc upon compilation of instrumented sources. This is useful when specific compiler settings have been set on the main Java/Groovy compiler for your buildscript and need to be carried over to the compilation of the instrumented sources. These are held within a closure named compiler.

The sourceCompatibility and targetCompatibility options will work only as long as your code is compatible with the selections. If your code uses language features above the language level specified OpenClover will fail to compile the code.

The Clover plugin defines the following convention properties in the clover closure:

Example

clover {
    excludes = ['**/SynchronizedMultiValueMap.java']

    // This is the default testIncludes configuration
    testIncludes = ['**/*Test.java', '**/*Spec.groovy']
    testExcludes = ['**/Mock*.java']

    targetPercentage = '85%'

    // Closure based syntax for additionalSourceSets and
    // additionalTestSourceSets is also supported. Both
    // srcDirs and classesDir properties are required.
    // The syntax allows the following to occur as many times
    // as necessary to define each additional sourceSet.
    // From version 3.0.0 and later the configuration is
    // requiring the Gradle 4.0 outputDir for each language
    // in the sourceSet. If you have Java and Groovy sourceSets
    // you may need to specify each language in the sourceSet
    // separately.
    additionalSourceSet {
        srcDirs = sourceSets.generatedCode.java.srcDirs
        classesDir = sourceSets.generatedCode.java.outputDir
    }
    additionalSourceSet {
        srcDirs = sourceSets.generatedCode.groovy.srcDirs
        classesDir = sourceSets.generatedCode.groovy.outputDir
    }
    additionalTestSourceSet {
        srcDirs = sourceSets.integrationTest.java.srcDirs,
        classesDir = sourceSets.integrationTest.java.outputDir
    }
    additionalTestSourceSet {
        srcDirs = sourceSets.integrationTest.groovy.srcDirs,
        classesDir = sourceSets.integrationTest.groovy.outputDir
    }

    compiler {
        encoding = 'UTF-8'

        // Override the Java Compiler source and target compatibility settings
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'

        // if the javac executable used by ant should be the same as the one used elsewhere.
        executable = file('/usr/local/java/jdk1.8.0_05')
        // used to add debug information for Spring applications
        debug = true
        additionalArgs = '-parameters'
        additionalGroovycOpts = [configscript: project.file('myConfigScript.groovy').absolutePath]
    }

    contexts {
        statement {
            name = 'log'
            //start of line, any amount of whitespace then _LOG. minimal match before finding );
            regexp = '^[\s]+_LOG\..*?\);'
        }

        method {
            name = 'main'
            regexp = 'public static void main\\(String args\\[\\]\\).*'
        }
        method {
            name = 'getters'
            regexp = 'public [^\\s]+ get[A-Z][^\\s]+\\(\\)'
            maxStatements = 1
        }
        method {
            name = 'setters'
            regexp = 'public void set[A-Z][^\\s]+\\(.+\\)'
            maxStatements = 1
        }
    }

    report {
        html = true
        pdf = true
        xml = true
        filter = 'log,main,getters,setters'

        // Support capturing test results from JUnix XML report
        testResultsDir = project.tasks.getByName('test').reports.junitXml.destination
        testResultsInclude = 'TEST-*.xml'

        // Support controlling current-report for OpenClover
        alwaysReport = false
        includeFailedTestCoverage = false
        numThreads = 2
        timeout = '30 seconds'

        // Clover report nested columns support
        columns {
            coveredMethods format: 'longbar', min: '75'
            coveredStatements format: '%'
            coveredBranches format: 'raw'
            totalPercentageCovered format: '%', scope: 'package'
        }

        // Clover history generation support
        // See Clover documentation for details of the values supported
        historical {
            enabled = true
            historyIncludes = 'clover-*.xml.gz'
            packageFilter = null
            from = null
            to = null

            added {
                range = 10
                interval = '3 weeks'
            }
            mover {
                threshold = 1
                range = 10
                interval = '3 weeks'
            }
            mover {
                threshold = 1
                range = 10
                interval = '3 months'
            }
            mover {
                threshold = 1
                range = 10
                interval = '1 year'
            }
        }
    }
}

Console Clover Coverage Reporting

When clover.report.xml=true the cloverGenerateReport will emit the following information at quiet logger level in the console log.

Project <project name> classes coverage
Files: ## Packages: ## Classes: ## LOC: ## NCLOC: ##
Methods coverage ##.##
Elements coverage ##.##
Statements coverage ##.##
Conditionals coverage ##.##

Project <project name> test classes coverage
Files: ## Packages: ## Classes: ## LOC: ## NCLOC: ##
Methods coverage ##.##
Elements coverage ##.##
Statements coverage ##.##
Conditionals coverage ##.##

In a multiproject configuration this will be emitted by each cloverGenerateReport task in the project hierarchy. This does not get emitted by the aggregate report.

Groovy Compiler Configuration Scripts

Thanks to a contribution by community member we now have more power over the Groovy compilation process. The specific problem this feature solved is the inability to process correctly with OpenClover the @CompileStatic and @TypeChecked annotations in the code.

The solution involves installing a small Groovy Compiler configuration script that can perform an AST transformation of the code when it is compiled by the tools. The following example can be used as a basis for such a solution. All you need to do is place a groovy file with this content in your project directory and reference the file name using the new additionalGroovycOpts configuration.

withConfig(configuration) {
    inline(phase: 'CONVERSION') { source, context, classNode ->
        source.ast.unit.classes.each { clazz ->
            println "Fixing $clazz.name"
            clazz.annotations.removeAll { annotation -> annotation.classNode.name in ['CompileStatic', 'TypeChecked'] }
        }
    }
}

Project Properties

The Clover plugin uses the following properties: