coverage

coverage

cobertura-plugin

[!NOTE|label:references:]

libs

def showCoverageReport( String xmlPath, Map targets = [:], Boolean failNoReports = true ) {
  Map<String, String> benchmarks = [
                                     conditional : '70, 0, 0' ,
                                            line : '80, 0, 0' ,
                                          method : '80, 0, 0' ,
                                          branch : '60, 0, 0'
                                   ]
  benchmarks = benchmarks << targets

  def file = findFiles( glob: xmlPath )
  if ( file.size() ) {
    String report = file.first().path
    println "coverage report file found in: ${report}"
    cobertura coberturaReportFile: report ,
              conditionalCoverageTargets: benchmarks.conditional ,
              lineCoverageTargets: benchmarks.line ,
              methodCoverageTargets: benchmarks.method ,
              failNoReports: failNoReports ,
              failUnhealthy: false,
              failUnstable: false,
              onlyStable: false,
              autoUpdateStability: true,
              autoUpdateHealth: false,
              enableNewApi: false

  } else {
    error "Could not find cobertura xml report in pattern: ${xmlPath}"
  }
}

Jenkinsfile

recordCoverage qualityGates: [ [criticality: 'NOTE' , integerThreshold: 30 , metric: 'MODULE' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'PACKAGE' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'FILE' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'CLASS' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'METHOD' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'LINE' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'BRANCH' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'INSTRUCTION' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'MUTATION' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'TEST_STRENGTH' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'COMPLEXITY' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'COMPLEXITY_MAXIMUM' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'LOC' , threshold: 30.0] , [criticality: 'NOTE' , integerThreshold: 30 , metric: 'TESTS' , threshold: 30.0] ], tools: [[parser: 'COBERTURA', pattern: '*.xml']]

recordCoverage checksAnnotationScope: 'ALL_LINES', enabledForFailure: true, ignoreParsingErrors: true, qualityGates: [ [baseline: 'MODIFIED_LINES', criticality: 'NOTE', metric: 'LINE', threshold: 0.001] ], skipSymbolicLinks: true, sourceCodeRetention: 'EVERY_BUILD', sourceDirectories: [[path: '../Src']], tools: [[parser: 'COBERTURA', pattern: 'a.xml']]

  • Cobertura code coverage report for jenkins pipeline jobs

    step([
          $class: 'CoberturaPublisher',
          autoUpdateHealth: false,
          autoUpdateStability: false,
          coberturaReportFile: '**/coverage.xml',
          failUnhealthy: false,
          failUnstable: false,
          maxNumberOfBuilds: 0,
          onlyStable: false,
          sourceEncoding: 'ASCII',
          zoomCoverageChart: false
       ])
    
  • When coverage goes down threshold is updated and the build is not marked as unstable

    node('my-node') {
      [...]
    
      stage('Publish coverage report') {
        archive "coverage.xml"
    
        cobertura(
          coberturaReportFile: "coverage.xml",
          onlyStable: false,
          failNoReports: true,
          failUnhealthy: false,
          failUnstable: false,
          autoUpdateHealth: true,
          autoUpdateStability: true,
          zoomCoverageChart: true,
          maxNumberOfBuilds: 0,
          lineCoverageTargets: '80, 80, 80',
          conditionalCoverageTargets: '80, 80, 80',
          classCoverageTargets: '80, 80, 80',
          fileCoverageTargets: '80, 80, 80',
        )
      }
    }
  • pipeline-library/vars/buildPlugin.groovy

    if (!skipTests) {
        junit('**/target/surefire-reports/**/*.xml,**/target/failsafe-reports/**/*.xml,**/target/invoker-reports/**/*.xml')
        if (first) {
          discoverReferenceBuild()
          // Default configuration for JaCoCo can be overwritten using a `jacoco` parameter (map).
          // Configuration see: https://www.jenkins.io/doc/pipeline/steps/code-coverage-api/#recordcoverage-record-code-coverage-results
          Map jacocoArguments = [tools: [[parser: 'JACOCO', pattern: '**/jacoco/jacoco.xml']], sourceCodeRetention: 'MODIFIED']
          if (params?.jacoco) {
            jacocoArguments.putAll(params.jacoco as Map)
          }
          recordCoverage jacocoArguments
    
          if (pit) {
            Map pitArguments = [tools: [[parser: 'PIT', pattern: '**/pit-reports/mutations.xml']], id: 'pit', name: 'Mutation Coverage', sourceCodeRetention: 'MODIFIED']
            pitArguments.putAll(pit)
            pitArguments.remove('skip')
            recordCoverage(pitArguments)
          }
        }
      }
    }

tips

[!NOTE|label:references:]

sample

libs


def showCoverageReport( String xmlPath, String sourcePath = '**/src', Map targets = [:] ) {
  Map<String, String> benchmarks = [
                                     conditional : '70, 0, 0' ,
                                            line : '80, 0, 0' ,
                                          method : '80, 0, 0' ,
                                          branch : '60, 0, 0'
                                   ]
  benchmarks = benchmarks << targets

  def file = findFiles( glob: xmlPath )
  if ( file.size() ) {
    String report = file.first().path
    println "coverage report file found: ${report}"

    discoverReferenceBuild()
    recordCoverage( name: 'Cobertura Coverage',
                    id: 'coverage',
                    tools: [[ parser: 'COBERTURA', pattern: xmlPath ]],
                    sourceDirectories: [[ path: sourcePath ]],
                    ignoreParsingErrors: true,
                    skipSymbolicLinks: false,
                    calculateDiffForChangeRequests: true,
                    failBuildIfCoverageDecreasedInChangeRequest: true,
                    sourceCodeRetention: 'EVERY_BUILD',
                    checksAnnotationScope: 'ALL_LINES',
                    qualityGates: [
                      [ threshold: benchmarks.line.split(',').first()   , metric: 'LINE'   , baseline: 'PROJECT'        , criticality: 'UNSTABLE' ] ,
                      [ threshold: 0.01                                 , metric: 'LINE'   , baseline: 'MODIFIED_LINES' , criticality: 'UNSTABLE' ] ,
                      [ threshold: benchmarks.branch.split(',').first() , metric: 'BRANCH' , baseline: 'PROJECT'        , criticality: 'UNSTABLE' ]
                    ]
                  )
  } else {
    error( "Could not find cobertura xml report in pattern: ${xmlPath}" )
  }
}

Jenkinsfile

[!NOTE|label:references:]

  • jenkinsci/jenkins/Jenkinsfile

    recordCoverage(tools: [[parser: 'JACOCO', pattern: 'coverage/target/site/jacoco-aggregate/jacoco.xml']],
    sourceCodeRetention: 'MODIFIED', sourceDirectories: [[path: 'core/src/main/java']])
  • prototyp-feedbackcomponent/Jenkinsfile

    // New Coverage Tool: Cobertura + Coverage Plugin
    recordCoverage qualityGates: [[metric: 'LINE', threshold: 1.0], [metric: 'BRANCH', threshold: 1.0]], tools: [[parser: 'COBERTURA', pattern: 'src/output/test/coverage/cobertura-coverage.xml']]

deprecated

[!NOTE|label:references:]

  • Code Coverage

    def publishCoverage( String xmlPath ) {
      def file = findFiles( glob: xmlPath )
    
      if ( file.size() ) {
        String report = file.first().path
        color.info( "coverage report file: ${report}" )
        archiveArtifacts artifacts: report
    
        publishCoverage adapters: [
                          cobertura( path: xmlPath, thresholds: [[ thresholdTarget: 'Conditional', unstableThreshold: 30.0 ]] )
                        ],
                        applyThresholdRecursively: true,
                        failBuildIfCoverageDecreasedInChangeRequest: false,
                        failNoReports: false,
                        failUnhealthy: false,
                        failUnstable: false,
                        skipPublishingChecks: false,
                        globalThresholds: [[ thresholdTarget: 'Conditional', unstableThreshold: 30.0 ]],
                        sourceDirectories: [[ path: '../Src' ]],
                        sourceFileResolver: sourceFiles('STORE_ALL_BUILD')
      } else {
        error( "Could not find cobertura xml report in pattern ${xmlPath}" )
      }
    }
    • #657 Stored source code files should use a unique path

      publishCoverage(
          adapters: [coberturaReportAdapter(mergeToOneReport: true, path: '**/*cobertura*.xml')],
          calculateDiffForChangeRequests: true,
          failNoReports: true,
          globalThresholds: [[thresholdTarget: 'Line', unstableThreshold: 89.0]]
      )
    • jenkins declerative pipeline - fail build when coverage drops

      def coverage = [
        'applyThresholdRecursively':true,
        'failBuildIfCoverageDecreasedInChangeRequest':true,
        /* ... etc ... */
      ]
      coverage.globalThresholds = [[
        failUnhealthy: false,
        thresholdTarget: 'File',
        unhealthyThreshold: 1.0,
        unstableThreshold: 0.0
      ]] //use your own values here
      def coverageFilePath = 'path-to-your-coverage-file'
      
      publishCoverage(
        adapters: [ coberturaAdapter(mergeToOneReport: true, path: coverageFilePath) ],
        applyThresholdRecursively: coverage.applyThresholdRecursively,
        failBuildIfCoverageDecreasedInChangeRequest: coverage.failBuildIfCoverageDecreasedInChangeRequest,
        failNoReports: coverage.failNoReports,
        failUnhealthy: coverage.failUnhealthy,
        failUnstable: coverage.failUnstable,
        globalThresholds: coverage.globalThresholds
      )

Last updated