coverage
cobertura-plugin
[!NOTE|label:references:]
- steps: Cobertura Plugin
- #89 When coverage goes down threshold is updated and the build is not marked as unstable
- Jenkins Cobertura Plugin - reset Ratcheting / autoUpdateHealth values | #124 Cannot reset ratcheted targets
- src/main/java/hudson/plugins/cobertura/CoberturaPublisher.java
PACKAGES
FILES
CLASSES
CONDITIONAL
LINE
METHOD
- Jenkins Pipeline With Python
- Static Code Analysis Using SonarQube and Jenkins
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
unstable build when thresholds decreased
[!NOTE|label:references:]
cobertura( ... autoUpdateStability: true, autoUpdateHealth: true, )
result
[Pipeline] cobertura 22:37:43 [Cobertura] Publishing Cobertura coverage report... 22:37:43 22:37:44 [Cobertura] Publishing Cobertura coverage results... 22:37:44 22:37:44 [Cobertura] Cobertura coverage report found. 22:37:44 22:37:45 [Cobertura] Code coverage enforcement failed for the following metrics: 22:37:45 22:37:45 [Cobertura] Conditionals's stability is 41.83 and set mininum stability is 41.85. 22:37:45 22:37:45 [Cobertura] Setting Build to unstable. 22:37:45 [Pipeline] }
coverage-plugin
[!NOTE|label:references:]
- Steps: Coverage Plugin
- jenkinsci/coverage-model
- Index of incrementals/edu/hm/hafner
public enum Metric
: src/main/java/edu/hm/hafner/coverage/Metric.java | qualityGates
- nodes that can have children
CONTAINER
MODULE
PACKAGE
FILE
CLASS
METHOD
- coverage values without children
LINE
BRANCH
INSTRUCTION
- additional metrics without children
MUTATION
TEST_STRENGTH
COMPLEXITY
COMPLEXITY_MAXIMUM
COMPLEXITY_DENSITY
LOC
TESTS
src/main/java/edu/hm/hafner/coverage/registry/ParserRegistry.java
- parser type:
COBERTURA
JACOCO
JUNIT
PIT
NUNIT
OPENCOVER
JUNIT
XUNIT
- sample workflow
sample
- ci.jenkins.io: Core » jenkins » master #6073 | #9194 Format 'admin' differently in the setup wizard for clarity | checkers
- app.codecov.io/gh/jenkinsci/coverage-plugin
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:]
-
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:]
-
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 )