├── src
├── main
│ ├── resources
│ │ ├── coverage
│ │ │ ├── taglib
│ │ │ ├── coverage-table.properties
│ │ │ ├── configuration.properties
│ │ │ ├── configuration.jelly
│ │ │ └── deprecated-coverage-table.jelly
│ │ ├── io
│ │ │ └── jenkins
│ │ │ │ └── plugins
│ │ │ │ └── coverage
│ │ │ │ ├── detector
│ │ │ │ ├── Messages.properties
│ │ │ │ └── AntPathReportDetector
│ │ │ │ │ └── config.jelly
│ │ │ │ ├── CoveragePublisher
│ │ │ │ ├── help-failNoReports.html
│ │ │ │ ├── help-failUnstable.html
│ │ │ │ ├── help-failUnhealthy.html
│ │ │ │ ├── help-calculateDiffForChangeRequests.html
│ │ │ │ ├── help-failBuildIfCoverageDecreasedInChangeRequest.html
│ │ │ │ ├── help-checksName.html
│ │ │ │ ├── help-skipPublishingChecks.html
│ │ │ │ ├── help-applyThresholdRecursively.html
│ │ │ │ ├── config.properties
│ │ │ │ ├── help-sourceCodeEncoding.html
│ │ │ │ ├── help-sourceDirectories.html
│ │ │ │ └── config.jelly
│ │ │ │ ├── adapter
│ │ │ │ ├── Messages.properties
│ │ │ │ ├── CoverageReportAdapterDescriptor
│ │ │ │ │ └── config.jelly
│ │ │ │ ├── istanbul-cobertura-to-standard.xsl
│ │ │ │ └── cobertura-to-standard.xsl
│ │ │ │ ├── model
│ │ │ │ ├── visualization
│ │ │ │ │ └── dashboard
│ │ │ │ │ │ └── CoverageColumn
│ │ │ │ │ │ ├── columnHeader.jelly
│ │ │ │ │ │ ├── config.properties
│ │ │ │ │ │ ├── config.jelly
│ │ │ │ │ │ └── column.jelly
│ │ │ │ ├── CoverageJobAction
│ │ │ │ │ └── floatingBox.jelly
│ │ │ │ ├── SourceViewModel
│ │ │ │ │ ├── index.properties
│ │ │ │ │ └── index.jelly
│ │ │ │ ├── CoverageViewModel
│ │ │ │ │ └── index.properties
│ │ │ │ └── Messages.properties
│ │ │ │ ├── source
│ │ │ │ ├── Messages.properties
│ │ │ │ ├── SourceFileResolver
│ │ │ │ │ └── config.jelly
│ │ │ │ └── Messages_zh_CN.properties
│ │ │ │ ├── targets
│ │ │ │ └── CoverageResult
│ │ │ │ │ └── index.properties
│ │ │ │ ├── CoverageColumn
│ │ │ │ └── column.jelly
│ │ │ │ ├── Messages_zh_CN.properties
│ │ │ │ ├── Messages.properties
│ │ │ │ ├── CoveragePullRequestMonitoringPortlet
│ │ │ │ └── monitor.jelly
│ │ │ │ ├── CoverageProjectAction
│ │ │ │ ├── nodata.jelly
│ │ │ │ └── floatingBox.jelly
│ │ │ │ ├── CoverageAction
│ │ │ │ └── summary.jelly
│ │ │ │ └── threshold
│ │ │ │ └── Threshold
│ │ │ │ └── config.jelly
│ │ ├── index.jelly
│ │ └── META-INF
│ │ │ └── hudson.remoting.ClassFilter
│ ├── webapp
│ │ ├── images
│ │ │ └── tab.png
│ │ ├── js
│ │ │ └── colors.js
│ │ └── css
│ │ │ └── style.css
│ └── java
│ │ └── io
│ │ └── jenkins
│ │ └── plugins
│ │ └── coverage
│ │ ├── CoverageFeatureConstants.java
│ │ ├── model
│ │ ├── visualization
│ │ │ ├── package-info.java
│ │ │ ├── dashboard
│ │ │ │ ├── package-info.java
│ │ │ │ ├── ProjectCoverage.java
│ │ │ │ ├── ProjectCoverageDelta.java
│ │ │ │ ├── ChangeCoverage.java
│ │ │ │ ├── ChangeCoverageDelta.java
│ │ │ │ └── IndirectCoverageChanges.java
│ │ │ ├── colorization
│ │ │ │ ├── package-info.java
│ │ │ │ ├── ColorId.java
│ │ │ │ ├── ColorScheme.java
│ │ │ │ ├── CoverageColorJenkinsId.java
│ │ │ │ ├── CoverageChangeTendency.java
│ │ │ │ ├── CoverageColorPalette.java
│ │ │ │ └── CoverageChangeLevel.java
│ │ │ └── charts
│ │ │ │ └── CoverageSeriesBuilder.java
│ │ ├── package-info.java
│ │ ├── exception
│ │ │ └── CodeDeltaException.java
│ │ ├── PackageCoverageNode.java
│ │ ├── SourceViewModel.java
│ │ ├── MethodCoverageNode.java
│ │ ├── CoverageLeaf.java
│ │ ├── FilePathValidator.java
│ │ ├── CoverageJobAction.java
│ │ └── SafeFraction.java
│ │ ├── targets
│ │ ├── RestResultWrapper.java
│ │ ├── CoverageTrend.java
│ │ ├── CoverageAggregationRule.java
│ │ ├── CoverageTrendTree.java
│ │ ├── HasName.java
│ │ ├── Chartable.java
│ │ ├── CoverageTreeElement.java
│ │ ├── CoverageElementRegister.java
│ │ └── CoverageElement.java
│ │ ├── detector
│ │ ├── Detectable.java
│ │ └── AntPathReportDetector.java
│ │ ├── adapter
│ │ ├── CoverageAdapter.java
│ │ ├── CoverageAdapterDescriptor.java
│ │ ├── converter
│ │ │ ├── JSONDocumentConverter.java
│ │ │ └── DocumentConverter.java
│ │ ├── JavaXMLCoverageReportAdapter.java
│ │ ├── CoverageReportAdapterDescriptor.java
│ │ ├── JSONCoverageReportAdapter.java
│ │ ├── JavaCoverageReportAdapterDescriptor.java
│ │ ├── XMLCoverageReportAdapter.java
│ │ └── CoberturaReportAdapter.java
│ │ ├── exception
│ │ └── CoverageException.java
│ │ ├── CoverageElementInitializer.java
│ │ ├── BuildUtils.java
│ │ ├── source
│ │ └── SourceFileResolver.java
│ │ ├── CompatibleObjectInputStream.java
│ │ └── CoverageProjectAction.java
└── test
│ ├── resources
│ ├── io
│ │ └── jenkins
│ │ │ └── plugins
│ │ │ └── coverage
│ │ │ ├── Security2376Test
│ │ │ └── jobs
│ │ │ │ └── fs
│ │ │ │ ├── builds
│ │ │ │ └── 1
│ │ │ │ │ ├── coverage-report
│ │ │ │ │ └── build.xml
│ │ │ │ └── config.xml
│ │ │ ├── model
│ │ │ ├── column-dsl.yaml
│ │ │ ├── cobertura-lower-coverage.xml
│ │ │ ├── AcuCobolParser.java.txt
│ │ │ ├── cobertura-higher-coverage.xml
│ │ │ └── cobertura-package-root.xml
│ │ │ ├── metrics
│ │ │ ├── steps
│ │ │ │ ├── column-metric-dsl.yaml
│ │ │ │ ├── coverage-publisher-details.checks-expected-result
│ │ │ │ ├── coverage-publisher-summary.checks-expected-result-pit
│ │ │ │ ├── coverage-publisher-summary.checks-expected-result
│ │ │ │ ├── coverage-publisher-quality-gate.checks-expected-result
│ │ │ │ ├── cobertura-lower-coverage.xml
│ │ │ │ ├── cobertura-higher-coverage.xml
│ │ │ │ └── job-dsl.yaml
│ │ │ ├── source
│ │ │ │ ├── tooltip.html
│ │ │ │ └── AcuCobolParser.java.txt
│ │ │ └── charts
│ │ │ │ └── chart.json
│ │ │ ├── sec1699.xml
│ │ │ └── cobertura-coverage.xml
│ └── design.puml
│ └── java
│ └── io
│ └── jenkins
│ └── plugins
│ └── coverage
│ ├── model
│ ├── visualization
│ │ ├── colorization
│ │ │ ├── CoverageColorJenkinsIdTest.java
│ │ │ ├── CoverageChangeTendencyTest.java
│ │ │ ├── CoverageLevelTest.java
│ │ │ └── CoverageChangeLevelTest.java
│ │ └── dashboard
│ │ │ ├── ChangeCoverageTest.java
│ │ │ ├── ProjectCoverageTest.java
│ │ │ ├── ChangeCoverageDeltaTest.java
│ │ │ ├── ProjectCoverageDeltaTest.java
│ │ │ └── IndirectCoverageChangesTest.java
│ ├── CoverageLeafTest.java
│ ├── SafeFractionTest.java
│ ├── DeclarativePipelineSupportITest.java
│ ├── JobDslITest.java
│ ├── PackageCoverageNodeTest.java
│ ├── testutil
│ │ └── JobStubs.java
│ ├── MethodCoverageNodeTest.java
│ ├── CoverageMetricTest.java
│ └── PluginArchitectureTest.java
│ ├── Security2376Test.java
│ ├── CoverageProcessorTest.java
│ └── CoverageCornerCaseTest.java
├── .mvn
├── maven.config
└── extensions.xml
├── img.png
├── images
├── step.png
├── summary.png
├── all-metrics.png
├── quality-gates.png
├── reportCC_screen.PNG
├── dashboard_screen.PNG
├── reportFile_screen.PNG
├── reportICC_screen.PNG
├── reportTree_screen.PNG
├── pit-coverage-checks.png
├── quality-gates-result.png
├── jacoco-coverage-checks.png
├── reportOverview_screen.PNG
├── pit-coverage-checks-annotations.png
└── jacoco-coverage-checks-annotations.png
├── .github
├── auto_assign.yml
├── workflows
│ ├── assign-pr.yml
│ ├── run-release-drafter.yml
│ ├── check-md-links.yml
│ ├── sync-labels.yml
│ ├── enforce-labels.yml
│ └── ci.yml
├── check-md-links.json
├── labels.yml
└── release-drafter.yml
├── bin
├── go.sh
├── skip.sh
├── clean.sh
├── release.sh
└── deploy.sh
├── .gitignore
├── README.md
├── etc
├── assertj-templates
│ ├── soft_assertions_entry_point_class_template.txt
│ ├── assertions_entry_point_class_template.txt
│ └── has_assertion_template.txt
└── Jenkinsfile.analysis
├── Jenkinsfile
└── LICENSE
/src/main/resources/coverage/taglib:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.mvn/maven.config:
--------------------------------------------------------------------------------
1 | -Pconsume-incrementals
2 | -Pmight-produce-incrementals
3 |
--------------------------------------------------------------------------------
/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/img.png
--------------------------------------------------------------------------------
/images/step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/step.png
--------------------------------------------------------------------------------
/images/summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/summary.png
--------------------------------------------------------------------------------
/images/all-metrics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/all-metrics.png
--------------------------------------------------------------------------------
/images/quality-gates.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/quality-gates.png
--------------------------------------------------------------------------------
/images/reportCC_screen.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/reportCC_screen.PNG
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/detector/Messages.properties:
--------------------------------------------------------------------------------
1 | AntPathDetector.displayName=Ant Path Detector
--------------------------------------------------------------------------------
/images/dashboard_screen.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/dashboard_screen.PNG
--------------------------------------------------------------------------------
/images/reportFile_screen.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/reportFile_screen.PNG
--------------------------------------------------------------------------------
/images/reportICC_screen.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/reportICC_screen.PNG
--------------------------------------------------------------------------------
/images/reportTree_screen.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/reportTree_screen.PNG
--------------------------------------------------------------------------------
/images/pit-coverage-checks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/pit-coverage-checks.png
--------------------------------------------------------------------------------
/images/quality-gates-result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/quality-gates-result.png
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-failNoReports.html:
--------------------------------------------------------------------------------
1 | Fail the build if no coverage reports found.
--------------------------------------------------------------------------------
/src/main/webapp/images/tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/src/main/webapp/images/tab.png
--------------------------------------------------------------------------------
/images/jacoco-coverage-checks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/jacoco-coverage-checks.png
--------------------------------------------------------------------------------
/images/reportOverview_screen.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/reportOverview_screen.PNG
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-failUnstable.html:
--------------------------------------------------------------------------------
1 | Fail the build if coverage is lower than stable threshold.
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-failUnhealthy.html:
--------------------------------------------------------------------------------
1 | Fail the step if coverage is lower than healthy threshold.
2 |
--------------------------------------------------------------------------------
/images/pit-coverage-checks-annotations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/pit-coverage-checks-annotations.png
--------------------------------------------------------------------------------
/images/jacoco-coverage-checks-annotations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/images/jacoco-coverage-checks-annotations.png
--------------------------------------------------------------------------------
/.github/auto_assign.yml:
--------------------------------------------------------------------------------
1 | addReviewers: false
2 | addAssignees: true
3 |
4 | assignees:
5 | - uhafner
6 |
7 | skipKeywords:
8 | - wip
9 |
10 | numberOfAssignees: 0
11 |
--------------------------------------------------------------------------------
/bin/go.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | (cd plugin; mvn clean install -DskipITs || { echo "Build failed"; exit 1; })
6 |
7 | $(dirname "$0")/deploy.sh code-coverage-api
8 |
9 |
--------------------------------------------------------------------------------
/bin/skip.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | (cd plugin; mvn clean install -Pskip || { echo "Build failed"; exit 1; })
6 |
7 | $(dirname "$0")/deploy.sh code-coverage-api
8 |
9 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-calculateDiffForChangeRequests.html:
--------------------------------------------------------------------------------
1 | If it is a change request build, calculate code coverage diff with a target branch build
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-failBuildIfCoverageDecreasedInChangeRequest.html:
--------------------------------------------------------------------------------
1 | Fail the build in case if code coverage decreased in comparison with the target branch
--------------------------------------------------------------------------------
/bin/clean.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | (cd plugin; mvn clean install -Djenkins.test.timeout=1000 || { echo "Build failed"; exit 1; })
6 |
7 | $(dirname "$0")/deploy.sh code-coverage-api
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/main/resources/coverage/coverage-table.properties:
--------------------------------------------------------------------------------
1 | changed.files=Show only changed files
2 | select.row=Please select a file in the table to open the source code
3 | no.sourcecode=No source code available for this file
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node
2 | node_modules
3 | target
4 | *.iml
5 | .classpath
6 | .project
7 | .settings
8 | pom.xml.versionsBackup
9 | pom.xml.releaseBackup
10 | release.properties
11 | .DS_Store
12 | .idea
13 | /package-lock.json
14 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/adapter/Messages.properties:
--------------------------------------------------------------------------------
1 | CoberturaReportAdapter.displayName=Cobertura
2 | JacocoReportAdapter.displayName=Jacoco
3 | IstanbulCoberturaReportAdapter.displayName=Istanbul (Cobertura reporter)
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/columnHeader.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | ${it.columnName}
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/source/Messages.properties:
--------------------------------------------------------------------------------
1 | SourceFileResolver.neverSave=never save source files
2 | SourceFileResolver.saveLast=save last build source files
3 | SourceFileResolver.saveAll=save all build source files
4 |
--------------------------------------------------------------------------------
/bin/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | git pull
4 | git push
5 | cd plugin
6 | mvn -B clean build-helper:parse-version release:prepare release:perform -DdevelopmentVersion=\${parsedVersion.majorVersion}.\${parsedVersion.nextMinorVersion}.0-SNAPSHOT
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/Security2376Test/jobs/fs/builds/1/coverage-report:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/code-coverage-api-plugin/HEAD/src/test/resources/io/jenkins/plugins/coverage/Security2376Test/jobs/fs/builds/1/coverage-report
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/source/SourceFileResolver/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-checksName.html:
--------------------------------------------------------------------------------
1 |
2 | If provided, and publishing checks enabled, the plugin will use this name when publishing results to corresponding
3 | SCM hosting platforms. If not, the default of "Code Coverage" will be used.
4 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/CoverageJobAction/floatingBox.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.github/workflows/assign-pr.yml:
--------------------------------------------------------------------------------
1 | name: 'Auto Assign PR'
2 |
3 | on: pull_request_target
4 |
5 | jobs:
6 | add-reviews:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: kentaro-m/auto-assign-action@v1.2.5
10 | with:
11 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
12 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-skipPublishingChecks.html:
--------------------------------------------------------------------------------
1 | If unchecked, the plugin will automatically publish the coverage to corresponding SCM platforms.
2 | For example, if you are using this feature for a GitHub organization project, the coverage will be published to GitHub through the Checks API.
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/CoverageFeatureConstants.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage;
2 |
3 |
4 | // this class will list all extra features
5 | // TODO may need a better solution :(
6 | public class CoverageFeatureConstants {
7 | public static final String FEATURE_SOURCE_FILE_PATH = "source-file-path";
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/source/Messages_zh_CN.properties:
--------------------------------------------------------------------------------
1 | SourceFileResolver.neverSave=\u6c38\u8fdc\u4e0d\u4fdd\u5b58\u6e90\u6587\u4ef6
2 | SourceFileResolver.saveLast=\u4fdd\u5b58\u4e0a\u4e00\u6b21\u6784\u5efa\u7684\u6e90\u6587\u4ef6
3 | SourceFileResolver.saveAll=\u4fdd\u5b58\u6240\u6709\u6784\u5efa\u7684\u6e90\u6587\u4ef6
4 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/targets/CoverageResult/index.properties:
--------------------------------------------------------------------------------
1 | reason.1=\
2 | You have specified source file storing level to never save.
3 |
4 | reason.2=\
5 | Code Coverage API plugin cannot found source files.
6 |
7 | reason.3=\
8 | You do not have sufficient permissions to view this file.
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Contains logic and models for visualizing code coverage.
3 | */
4 | @DefaultAnnotation(NonNull.class)
5 | package io.jenkins.plugins.coverage.model.visualization;
6 |
7 | import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
8 | import edu.umd.cs.findbugs.annotations.NonNull;
9 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/SourceViewModel/index.properties:
--------------------------------------------------------------------------------
1 | reason.1=\
2 | You did not enable storing of source files (see parameter 'sourceFiles').
3 |
4 | reason.2=\
5 | Code Coverage API plugin did not find the source files.
6 |
7 | reason.3=\
8 | You do not have sufficient permissions to view source files.
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-applyThresholdRecursively.html:
--------------------------------------------------------------------------------
1 | By default, this plugins will only apply threshold at report level. If this option is set to true, it will apply threshold to all levels.
2 |
3 | For example, if this option is true, and if you have specified a `Line` threshold, this plugin will check all modules, directories, files...
--------------------------------------------------------------------------------
/.github/workflows/run-release-drafter.yml:
--------------------------------------------------------------------------------
1 | name: 'Invoke Release Drafter'
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - main
8 |
9 | jobs:
10 | update_release_draft:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: release-drafter/release-drafter@v5.25.0
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | The Code Coverage API plug-in reached end-of-life. All functionality has been integrated
4 | into the
Coverage Plugin .
5 | Please migrate your steps to the new
recordCoverage step provided by the Coverage Plugin.
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/config.properties:
--------------------------------------------------------------------------------
1 | title.sourceCodeEncoding=Source Code Encoding
2 | description.sourceCodeEncoding=Encoding of your source code.
3 | title.sourceDirectories=Source Directories
4 | description.sourceDirectories=Additional paths to the source code if not in the root of the workspace \
5 | (or outside the workspace).
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/CoverageViewModel/index.properties:
--------------------------------------------------------------------------------
1 | coverage.details.title=Coverage details by {0}
2 | reason.1=\
3 | You did not enable storing of source files (see parameter 'sourceFiles').
4 | reason.2=\
5 | Code Coverage API plugin did not find the source files.
6 | reason.3=\
7 | You do not have sufficient permissions to view source files.
8 |
--------------------------------------------------------------------------------
/.github/check-md-links.json:
--------------------------------------------------------------------------------
1 | {
2 | "httpHeaders": [
3 | {
4 | "urls": ["https://github.com/", "https://guides.github.com/", "https://help.github.com/", "https://docs.github.com/", "https://classroom.github.com"],
5 | "headers": {
6 | "Accept-Encoding": "zstd, br, gzip, deflate"
7 | }
8 | }
9 | ],
10 | "aliveStatusCodes": [200, 500, 503]
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/RestResultWrapper.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.targets;
2 |
3 | import hudson.model.Api;
4 |
5 | public class RestResultWrapper {
6 | private Object o;
7 |
8 | public RestResultWrapper(Object o) {
9 | this.o = o;
10 | }
11 |
12 | public Api getApi() {
13 | return new Api(o);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/config.properties:
--------------------------------------------------------------------------------
1 | title.columnName=Column Name
2 | title.coverageType=Coverage Type
3 | description.coverageType=Choose which code coverage type should be shown in the column..
4 | title.coverageMetric=Coverage Metric
5 | description.coverageMetric=Choose which code coverage metric should be shown in the column.
6 |
--------------------------------------------------------------------------------
/.github/workflows/check-md-links.yml:
--------------------------------------------------------------------------------
1 | name: 'Check Markdown links'
2 |
3 | on: push
4 |
5 | jobs:
6 | markdown-link-check:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 | - uses: gaurav-nelson/github-action-markdown-link-check@v1
11 | with:
12 | check-modified-files-only: 'no'
13 | config-file: '.github/check-md-links.json'
14 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Contains logic and models for visualizing code coverage within a dashboard column.
3 | */
4 | @DefaultAnnotation(NonNull.class)
5 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
6 |
7 | import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
8 | import edu.umd.cs.findbugs.annotations.NonNull;
9 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Provides colors and operations on them to be used in order to visualize coverage.
3 | */
4 | @DefaultAnnotation(NonNull.class)
5 | package io.jenkins.plugins.coverage.model.visualization.colorization;
6 |
7 | import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
8 | import edu.umd.cs.findbugs.annotations.NonNull;
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## This plugin reached end-of-life.
2 |
3 | All functionality has been integrated into the [Coverage Plugin](https://github.com/jenkinsci/coverage-plugin). Please use that plugin instead. It is available in the Jenkins update center.
4 |
5 | If you are still using the old and deprecated step `publishCoverage` then you still can use this plugin. But please migrate to the new step `recordCoverage` as soon as possible.
6 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * New coverage model that tries to replace all the existing functionality with a more versatile model.
3 | *
4 | * @author Ullrich Hafner
5 | */
6 | @DefaultAnnotation(NonNull.class)
7 | package io.jenkins.plugins.coverage.model;
8 |
9 | import edu.umd.cs.findbugs.annotations.DefaultAnnotation;
10 | import edu.umd.cs.findbugs.annotations.NonNull;
11 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoverageColumn/column.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ${it.getCoverage(job)}
6 |
7 |
8 | ${it.getCoverage(job)}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/etc/assertj-templates/soft_assertions_entry_point_class_template.txt:
--------------------------------------------------------------------------------
1 | package ${package};
2 |
3 | /**
4 | * Entry point for soft assertions of different data types.
5 | */
6 | @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("NM")
7 | @javax.annotation.Generated(value="assertj-assertions-generator")
8 | public class SoftAssertions extends org.assertj.core.api.AutoCloseableSoftAssertions {
9 | ${all_assertions_entry_points}
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-sourceCodeEncoding.html:
--------------------------------------------------------------------------------
1 |
2 | In order to correctly show all your affected source code files in the detail views,
3 | the plugin must open these files with the correct character encoding (UTF-8, ISO-8859-1, etc.).
4 | If you leave this field empty then the default encoding of the platform will be used. This might work but
5 | is not recommended.
6 |
7 |
--------------------------------------------------------------------------------
/.mvn/extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | io.jenkins.tools.incrementals
4 | git-changelist-maven-extension
5 | 1.7
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/model/column-dsl.yaml:
--------------------------------------------------------------------------------
1 | jobs:
2 | - script: >
3 | listView("dsl-view") {
4 | jobs {
5 | regex("^bar")
6 | }
7 | recurse(true)
8 | columns {
9 | coverageColumn()
10 | status()
11 | weather()
12 | name()
13 | lastSuccess()
14 | lastFailure()
15 | lastDuration()
16 | testResult(1)
17 | buildButton()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.github/workflows/sync-labels.yml:
--------------------------------------------------------------------------------
1 | name: Sync labels
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - main
7 | paths:
8 | - .github/labels.yml
9 | - .github/workflows/sync-labels.yml
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: micnncim/action-label-syncer@v1.3.0
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 | with:
20 | manifest: .github/labels.yml
21 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/Messages_zh_CN.properties:
--------------------------------------------------------------------------------
1 | CoverageColumn.ColumnName=\u8986\u76d6\u7387
2 | CoveragePublisher.displayName=\u53d1\u5e03\u4ee3\u7801\u8986\u76d6\u7387\u62a5\u544a
3 | CoverageAction.displayName=\u4ee3\u7801\u8986\u76d6\u7387\u62a5\u544a
4 | CoverageProjectAction.displayName=\u4ee3\u7801\u8986\u76d6\u7387\u62a5\u544a
5 | CoverageProcessor.healthReportDescriptionTemplate=\u8986\u76d6\u7387\u5206\u6570\u4e3a {0}%
6 | CoverageColumn.Caption=\u884c\u8986\u76d6\u7387
7 | CoverageColumn.CoverageEmpty=none
--------------------------------------------------------------------------------
/src/main/webapp/js/colors.js:
--------------------------------------------------------------------------------
1 | getJenkinsColors = function (colors) {
2 | // TODO: also handle HSL colors and parse them to hex in order to use dark mode colors
3 | const colorHexMapping = new Map;
4 | colors.forEach(function (jenkinsId) {
5 | const colorHex = getComputedStyle(document.body).getPropertyValue(jenkinsId);
6 | if (colorHex.match(/^#[a-fA-F0-9]{6}$/) !== null) {
7 | colorHexMapping.set(jenkinsId, colorHex);
8 | }
9 | })
10 | return colorHexMapping;
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/column-metric-dsl.yaml:
--------------------------------------------------------------------------------
1 | jobs:
2 | - script: >
3 | listView("dsl-view") {
4 | jobs {
5 | regex("^bar")
6 | }
7 | recurse(true)
8 | columns {
9 | coverageMetricColumn()
10 | status()
11 | weather()
12 | name()
13 | lastSuccess()
14 | lastFailure()
15 | lastDuration()
16 | testResult(1)
17 | buildButton()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/bin/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | JENKINS_HOME=../docker/volumes/jenkins-home
6 |
7 | echo "Installing plugin ${1} in $JENKINS_HOME"
8 | rm -rf $JENKINS_HOME/plugins/${1}*
9 | cp -fv plugin/target/${1}.hpi $JENKINS_HOME/plugins/${1}.jpi
10 |
11 | CURRENT_UID="$(id -u):$(id -g)"
12 | export CURRENT_UID
13 | IS_RUNNING=$(docker-compose ps -q jenkins-controller)
14 | if [[ "$IS_RUNNING" != "" ]]; then
15 | docker-compose restart
16 | echo "Restarting Jenkins (docker compose with user ID ${CURRENT_UID}) ..."
17 | fi
18 |
19 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorId.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | /**
4 | * Provides IDs for colors which are used within this plugin in order to separate the color palette from the logic.
5 | *
6 | * @author Florian Orendi
7 | */
8 | public enum ColorId {
9 | INSUFFICIENT,
10 | VERY_BAD,
11 | BAD,
12 | INADEQUATE,
13 | AVERAGE,
14 | GOOD,
15 | VERY_GOOD,
16 | EXCELLENT,
17 |
18 | BLACK,
19 | WHITE
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/detector/AntPathReportDetector/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Ant Style Path
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.github/workflows/enforce-labels.yml:
--------------------------------------------------------------------------------
1 | name: Enforce PR labels
2 | on:
3 | pull_request:
4 | types: [opened, labeled, unlabeled, synchronize]
5 | jobs:
6 | label:
7 | runs-on: ubuntu-latest
8 | permissions:
9 | issues: write
10 | pull-requests: write
11 | steps:
12 | - uses: mheap/github-action-required-labels@v5
13 | with:
14 | mode: minimum
15 | count: 1
16 | labels: "bug,feature,enhancement,breaking,tests,documentation,internal,dependencies"
17 | message: "Maintainer needs to assign at least one label before merge"
18 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/detector/Detectable.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.detector;
2 |
3 | import io.jenkins.plugins.coverage.adapter.CoverageReportAdapter;
4 |
5 | import java.io.File;
6 |
7 | /**
8 | * Coverage report will be able to be automatically found and match.
9 | *
10 | * It should be implemented by Descriptor of {@link CoverageReportAdapter}
11 | *
12 | */
13 | public interface Detectable {
14 | /**
15 | * @param file file be detected
16 | * @return true if file match the rule
17 | */
18 | boolean detect(File file);
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/Messages.properties:
--------------------------------------------------------------------------------
1 | CoveragePublisher.displayName=Publish Coverage Report [deprecated]
2 | CoverageAction.displayName=Coverage Report
3 | CoverageProjectAction.displayName=Coverage Report
4 | CoverageProcessor.healthReportDescriptionTemplate=Coverage Healthy score is {0}%
5 | CoverageColumn.Caption=Line Coverage
6 | CoverageColumn.ColumnName=Code Coverage
7 | CoverageColumn.CoverageEmpty=none
8 | FieldValidator.Error.DefaultEncoding=Encoding must be a supported encoding of the Java platform, see: \
9 | java.nio.charset.Charset
10 |
--------------------------------------------------------------------------------
/etc/assertj-templates/assertions_entry_point_class_template.txt:
--------------------------------------------------------------------------------
1 | package ${package};
2 |
3 | /**
4 | * Entry point for assertions of different data types. Each method in this class is a static factory for the
5 | * type-specific assertion objects.
6 | */
7 | @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("NM")
8 | @javax.annotation.Generated(value="assertj-assertions-generator")
9 | public class Assertions extends org.assertj.core.api.Assertions {
10 | ${all_assertions_entry_points}
11 | /**
12 | * Creates a new {@link Assertions}.
13 | */
14 | protected Assertions() {
15 | // empty
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/CoverageAdapter.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 | import hudson.ExtensionPoint;
4 | import hudson.model.Describable;
5 | import hudson.model.Descriptor;
6 | import jenkins.model.Jenkins;
7 |
8 | import java.io.Serializable;
9 |
10 | public abstract class CoverageAdapter implements ExtensionPoint, Describable, Serializable {
11 |
12 |
13 | @SuppressWarnings("unchecked")
14 | @Override
15 | public Descriptor getDescriptor() {
16 | return Jenkins.get().getDescriptorOrDie(getClass());
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/help-sourceDirectories.html:
--------------------------------------------------------------------------------
1 |
2 | Since the plugin also reads the affected source code files it needs to copy these files from the agent to the
3 | controller. If these files are not part of the workspace (or checked out into a sub folder of the workspace) they
4 | are not automatically found. So you can add one or more source code directories where the plugin tries to find
5 | these files. Note, that due to security restrictions additional paths outside the workspace need to be registered
6 | in Jenkins system configuration before they can be used here.
7 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/exception/CodeDeltaException.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.exception;
2 |
3 | /**
4 | * Exception which is thrown when preprocessing the code delta failed.
5 | *
6 | * @author Florian Orendi
7 | */
8 | public class CodeDeltaException extends Exception {
9 | private static final long serialVersionUID = -7255072653278584604L;
10 |
11 | /**
12 | * Constructor which creates an exception with a message.
13 | *
14 | * @param message
15 | * The message
16 | */
17 | public CodeDeltaException(final String message) {
18 | super(message);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/CoverageAdapterDescriptor.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 | import hudson.DescriptorExtensionList;
4 | import hudson.model.Descriptor;
5 | import jenkins.model.Jenkins;
6 |
7 | public class CoverageAdapterDescriptor extends Descriptor {
8 |
9 | public CoverageAdapterDescriptor(Class extends CoverageAdapter> clazz) {
10 | super(clazz);
11 | }
12 |
13 | public static DescriptorExtensionList> all() {
14 | return Jenkins.get().getDescriptorList(CoverageAdapter.class);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorScheme.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | /**
4 | * Represents different types of color schemes that can be selected in order to load the matching color palette.
5 | *
6 | * @author Florian Orendi
7 | */
8 | public enum ColorScheme {
9 |
10 | /**
11 | * The default colors.
12 | */
13 | DEFAULT,
14 | /**
15 | * Colors which are used if the dark mode is activated.
16 | */
17 | DARK_MODE,
18 | /**
19 | * Colors which are usable in case of color blindness.
20 | */
21 | COLOR_BLINDNESS
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/Security2376Test/jobs/fs/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | true
9 | false
10 | false
11 | false
12 |
13 | false
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties:
--------------------------------------------------------------------------------
1 | Coverage.Not.Available=n/a
2 | Coverage.Link.Name=Coverage Report
3 | Coverage.Title=Coverage of ''{0}''
4 | Coverage_Column=Coverage [deprecated]
5 | Project_Coverage_Type=Project Coverage
6 | Project_Coverage_Delta_Type=Project Coverage Delta
7 | Change_Coverage_Type=Change Coverage
8 | Change_Coverage_Delta_Type=Change Coverage Delta
9 | Indirect_Coverage_Changes_Type=Indirect Coverage Changes
10 |
11 | Column.File=File
12 | Column.Package=Package
13 | Column.LineCoverage=Line
14 | Column.DeltaLineCoverage=Line {0}
15 | Column.BranchCoverage=Branch
16 | Column.DeltaBranchCoverage=Branch {0}
17 | Column.LinesOfCode=LOC
18 |
--------------------------------------------------------------------------------
/src/test/resources/design.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 |
3 | skinparam componentStyle uml2
4 | skinparam component {
5 | BorderColor #a0a0a0
6 | BackgroundColor #f8f8f8
7 | }
8 |
9 | [Steps] <<..metrics.steps>>
10 | [Color] <<..metrics.color>>
11 | [Source] <<..metrics.source>>
12 | [Charts] <<..metrics.charts>>
13 | [Model] <<..metrics.model>>
14 | [Restapi] <<..metrics.restapi>>
15 |
16 | [Restapi] --> [Model]
17 | [Restapi] --> [Source]
18 |
19 | [Steps] --> [Model]
20 | [Steps] --> [Color]
21 | [Steps] --> [Source]
22 | [Steps] --> [Charts]
23 | [Steps] --> [Restapi]
24 |
25 | [Charts] --> [Color]
26 | [Charts] --> [Model]
27 |
28 | [Source] --> [Model]
29 |
30 | [Model] --> [Color]
31 |
32 |
33 | @enduml
34 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/exception/CoverageException.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.exception;
2 |
3 | public class CoverageException extends Exception {
4 |
5 | public CoverageException() {
6 | }
7 |
8 | public CoverageException(String message) {
9 | super(message);
10 | }
11 |
12 | public CoverageException(String message, Throwable cause) {
13 | super(message, cause);
14 | }
15 |
16 | public CoverageException(Throwable cause) {
17 | super(cause);
18 | }
19 |
20 | public CoverageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
21 | super(message, cause, enableSuppression, writableStackTrace);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePullRequestMonitoringPortlet/monitor.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/converter/JSONDocumentConverter.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter.converter;
2 |
3 | import org.w3c.dom.Document;
4 |
5 | import com.fasterxml.jackson.databind.JsonNode;
6 |
7 | import io.jenkins.plugins.coverage.exception.CoverageException;
8 |
9 | public abstract class JSONDocumentConverter extends DocumentConverter {
10 |
11 |
12 | /**
13 | *
14 | * @param report JSON format report
15 | * @param document document that the report will convert to
16 | * @return standard format document converted by JSON format report
17 | */
18 | @Override
19 | protected abstract Document convert(JsonNode report, Document document) throws CoverageException;
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/sec1699.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | ]>
5 |
6 |
7 | fedepell
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | &xxe;
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/JavaXMLCoverageReportAdapter.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 | import org.w3c.dom.Document;
4 |
5 | import io.jenkins.plugins.coverage.adapter.parser.JavaCoverageParser;
6 | import io.jenkins.plugins.coverage.exception.CoverageException;
7 | import io.jenkins.plugins.coverage.targets.CoverageResult;
8 |
9 | public abstract class JavaXMLCoverageReportAdapter extends XMLCoverageReportAdapter {
10 |
11 |
12 | public JavaXMLCoverageReportAdapter(final String path) {
13 | super(path);
14 | }
15 |
16 | @Override
17 | public CoverageResult parseToResult(final Document document, final String reportName) throws CoverageException {
18 | return new JavaCoverageParser(reportName).parse(document);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoverageProjectAction/nodata.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | No valid coverage data available
7 | Some Reasons are there:
8 |
9 | Only coverage data of last succeed build will be shown in there.
10 | If you previous builds are all unstable or failed, you won't be able to see anything.
11 | You can go to the build's page to see coverage data for each build.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsIdTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static io.jenkins.plugins.coverage.model.visualization.colorization.CoverageColorJenkinsId.*;
6 | import static org.assertj.core.api.Assertions.*;
7 |
8 | /**
9 | * Test class for {@link CoverageColorJenkinsId}.
10 | *
11 | * @author Florian Orendi
12 | */
13 | class CoverageColorJenkinsIdTest {
14 |
15 | @Test
16 | void shouldGetAllIds() {
17 | assertThat(getAll().size()).isEqualTo(values().length);
18 | }
19 |
20 | @Test
21 | void shouldGetColorId() {
22 | assertThat(GREEN.getJenkinsColorId()).isEqualTo("--green");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/source/tooltip.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Killed Mutations:
4 |
5 | changed conditional boundary
6 | (org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator)
7 |
8 | negated conditional (org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator)
9 |
10 |
11 |
12 |
13 |
Survived Mutations:
14 |
15 | Replaced integer addition with subtraction
16 | (org.pitest.mutationtest.engine.gregor.mutators.MathMutator)
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | def configurations = [
2 | [ platform: "linux", jdk: "21" ],
3 | [ platform: "windows", jdk: "17" ]
4 | ]
5 |
6 | def params = [
7 | failFast: false,
8 | configurations: configurations,
9 | checkstyle: [qualityGates: [[threshold: 1, type: 'NEW', unstable: true]],
10 | filters:[includePackage('io.jenkins.plugins.coverage.metrics')]],
11 | pmd: [qualityGates: [[threshold: 1, type: 'NEW', unstable: true]],
12 | filters:[includePackage('io.jenkins.plugins.coverage.metrics')]],
13 | spotbugs: [qualityGates: [[threshold: 1, type: 'NEW', unstable: true]],
14 | filters:[includePackage('io.jenkins.plugins.coverage.metrics')]],
15 | jacoco: [sourceCodeRetention: 'MODIFIED', sourceDirectories: [[path: 'plugin/src/main/java']]]
16 | ]
17 |
18 | buildPlugin(params)
19 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/CoverageTrend.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.targets;
2 |
3 | import org.kohsuke.stapler.export.Exported;
4 | import org.kohsuke.stapler.export.ExportedBean;
5 |
6 | import java.io.Serializable;
7 | import java.util.List;
8 |
9 | @ExportedBean
10 | public class CoverageTrend implements Serializable {
11 |
12 | private String buildName;
13 | private List elements;
14 |
15 |
16 | public CoverageTrend(String buildName, List elements) {
17 | this.buildName = buildName;
18 | this.elements = elements;
19 | }
20 |
21 | @Exported
22 | public String getBuildName() {
23 | return buildName;
24 | }
25 |
26 | @Exported
27 | public List getElements() {
28 | return elements;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/CoverageReportAdapterDescriptor.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 |
4 | import io.jenkins.plugins.coverage.targets.CoverageElement;
5 |
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 |
10 | public class CoverageReportAdapterDescriptor
11 | extends CoverageAdapterDescriptor {
12 |
13 | public CoverageReportAdapterDescriptor(Class extends CoverageReportAdapter> clazz) {
14 | super(clazz);
15 | }
16 |
17 | public List getCoverageElements() {
18 | return Collections.emptyList();
19 | }
20 |
21 | public String getCoverageElementType() {
22 | return CoverageElement.COVERAGE_ELEMENT_TYPE_NONE;
23 | }
24 |
25 | public boolean defaultMergeToOneReport() {
26 | return false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/resources/coverage/configuration.properties:
--------------------------------------------------------------------------------
1 | parser.title=Code Coverage Tools
2 | parser.add=Add Tool
3 |
4 | qualityGates.add=Add Quality Gate
5 | qualityGates.title=Quality gates
6 | qualityGates.description=You can define an arbitrary number of quality gates that will be evaluated after a build. \
7 | If a quality gate is not passed then the build can be set to unstable or failed, respectively.
8 |
9 | title.id=Custom ID
10 | title.name=Custom Name
11 | skipPublishingChecks.title=Skip publishing of checks to SCM hosting platforms
12 | checksName.title=Checks name
13 | checksAnnotationScope.title=Select the scope of source code annotations
14 | failOnError.title=Fail the step if errors have been reported during the execution
15 | title.enabledForFailure=Enable recording for failed builds
16 | title.skipSymbolicLinks=Skip symbolic links when searching for files
17 | sourceCodeRetention.title=Source Code Retention Strategy
18 |
19 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/PackageCoverageNode.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | /**
4 | * A {@link CoverageNode} for a specific package. It converts a package structure to a corresponding path structure.
5 | *
6 | * @author Ullrich Hafner
7 | */
8 | public class PackageCoverageNode extends CoverageNode {
9 | private static final long serialVersionUID = 8236436628673022634L;
10 |
11 | /**
12 | * Creates a new coverage item node with the given name.
13 | *
14 | * @param name
15 | * the human-readable name of the node
16 | */
17 | public PackageCoverageNode(final String name) {
18 | super(CoverageMetric.PACKAGE, name);
19 | }
20 |
21 | @Override
22 | public String getPath() {
23 | return mergePath(getName().replaceAll("\\.", "/"));
24 | }
25 |
26 | @Override
27 | protected CoverageNode copyEmpty() {
28 | return new PackageCoverageNode(getName());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/hudson.remoting.ClassFilter:
--------------------------------------------------------------------------------
1 | gnu.trove.impl.hash.THash
2 | gnu.trove.impl.hash.TIntHash
3 | gnu.trove.impl.hash.TPrimitiveHash
4 | gnu.trove.map.hash.TIntObjectHashMap
5 |
6 | org.apache.commons.lang3.math.Fraction
7 |
8 | edu.hm.hafner.coverage.ClassNode
9 | edu.hm.hafner.coverage.ContainerNode
10 | edu.hm.hafner.coverage.Coverage
11 | edu.hm.hafner.coverage.CyclomaticComplexity
12 | edu.hm.hafner.coverage.FileNode
13 | edu.hm.hafner.coverage.FractionValue
14 | edu.hm.hafner.coverage.IntegerValue
15 | edu.hm.hafner.coverage.LinesOfCode
16 | edu.hm.hafner.coverage.MethodNode
17 | edu.hm.hafner.coverage.Metric
18 | edu.hm.hafner.coverage.ModuleNode
19 | edu.hm.hafner.coverage.Mutation
20 | edu.hm.hafner.coverage.MutationStatus
21 | edu.hm.hafner.coverage.Mutator
22 | edu.hm.hafner.coverage.Node
23 | edu.hm.hafner.coverage.PackageNode
24 | edu.hm.hafner.coverage.Percentage
25 | edu.hm.hafner.coverage.SafeFraction
26 | edu.hm.hafner.coverage.Value
27 |
28 | java.util.ImmutableCollections$ListN
29 | java.util.ImmutableCollections$SetN
30 | java.util.ImmutableCollections$Set12
31 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/Security2376Test.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage;
2 |
3 | import hudson.model.FreeStyleProject;
4 | import org.junit.Rule;
5 | import org.junit.Test;
6 | import org.jvnet.hudson.test.JenkinsRule;
7 | import org.jvnet.hudson.test.recipes.LocalData;
8 |
9 | import java.io.IOException;
10 |
11 | public class Security2376Test {
12 | @Rule
13 | public JenkinsRule j = new JenkinsRule();
14 |
15 | @LocalData
16 | @Test(expected = SecurityException.class)
17 | public void testDeserialization() throws IOException, ClassNotFoundException {
18 | // coverage-report is just a serialized empty IdentityHashMap, not on the serialization allowlist as of Jenkins 2.303
19 | final FreeStyleProject fs = (FreeStyleProject) j.jenkins.getItemByFullName("fs");
20 | CoverageProcessor.recoverCoverageResult(fs.getBuild("1"));
21 | // Without the fix, this fails with:
22 | // Caused by: java.lang.ClassCastException: java.util.IdentityHashMap cannot be cast to io.jenkins.plugins.coverage.targets.CoverageResult
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-details.checks-expected-result:
--------------------------------------------------------------------------------
1 | #### Project coverage details
2 |
3 | ||Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage
4 | |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:
5 | :feet: **Overall project**|100.00% (1/1)|100.00% (4/4)|70.00% (7/10)|83.33% (15/18)|95.10% (97/102)|91.02% (294/323)|93.97% (109/116)|93.33% (1260/1350)
6 | :chart_with_upwards_trend: **Overall project (difference to reference job)**|+0.00%|-50.00% :arrow_down:|n/a|n/a|n/a|+50.00% :arrow_up:|n/a|n/a
7 | :feet: **Modified files**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a
8 | :chart_with_upwards_trend: **Modified files (difference to overall project)**|n/a|n/a|n/a|n/a|n/a|+50.00% :arrow_up:|n/a|n/a
9 | :feet: **Modified code lines**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a
10 | :chart_with_upwards_trend: **Modified code lines (difference to modified files)**|n/a|n/a|n/a|n/a|n/a|+50.00% :arrow_up:|n/a|n/a
11 | :feet: **Indirect changes**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a
12 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/adapter/CoverageReportAdapterDescriptor/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ${%Report File Path}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/charts/chart.json:
--------------------------------------------------------------------------------
1 | {
2 | "domainAxisLabels": [
3 | "#1", "#2"
4 | ],
5 | "buildNumbers": [
6 | 1, 2
7 | ],
8 | "series": [
9 | {
10 | "name": "Line Coverage",
11 | "type": "line",
12 | "symbol": "circle",
13 | "data": [
14 | 50.0, 25.0
15 | ],
16 | "itemStyle": {
17 | "color": "--green",
18 | "borderColor": "#ffffff",
19 | "borderWidth": 0
20 | },
21 | "stack": "",
22 | "areaStyle": {
23 | "normal": true
24 | }
25 | },
26 | {
27 | "name": "Branch Coverage",
28 | "type": "line",
29 | "symbol": "circle",
30 | "data": [
31 | 75.0, 25.0
32 | ],
33 | "itemStyle": {
34 | "color": "--dark-green",
35 | "borderColor": "#ffffff",
36 | "borderWidth": 0
37 | },
38 | "stack": "",
39 | "areaStyle": {
40 | "normal": true
41 | }
42 | }
43 | ],
44 | "domainAxisItemName": "Build",
45 | "integerRangeAxis": false,
46 | "rangeMax": 100.0,
47 | "rangeMin": 25.0
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result-pit:
--------------------------------------------------------------------------------
1 | #### Summary for modified lines
2 | - 1 line has been modified
3 | - all lines are covered
4 | - 1 mutation survived (of 3)
5 | #### Overview by baseline
6 |
7 | * **[Overall project (difference to reference job)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#overview)**
8 | * Line Coverage: 93.84% (198/211) - Delta: +50.00%
9 | * Mutation Coverage: 90.24% (222/246)
10 | * Lines of Code: 211
11 | * **[Modified files (difference to overall project)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#modifiedFilesCoverage)**
12 | * Line Coverage: 50.00% (1/2) - Delta: +50.00%
13 | * **[Modified code lines (difference to modified files)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#modifiedLinesCoverage)**
14 | * Line Coverage: 50.00% (1/2) - Delta: +50.00%
15 | * **[Indirect changes](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#indirectCoverage)**
16 | * Line Coverage: 50.00% (1/2)
17 |
18 | #### Quality Gates Summary
19 |
20 | No active quality gates.
21 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.charts;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import edu.hm.hafner.echarts.SeriesBuilder;
7 |
8 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
9 |
10 | /**
11 | * Builds one x-axis point for the series of a line chart showing the line and branch coverage of a project.
12 | *
13 | * @author Ullrich Hafner
14 | */
15 | public class CoverageSeriesBuilder extends SeriesBuilder {
16 | static final String LINE_COVERAGE = "line";
17 | static final String BRANCH_COVERAGE = "branch";
18 |
19 | @Override
20 | protected Map computeSeries(final CoverageBuildAction coverageBuildAction) {
21 | Map series = new HashMap<>();
22 |
23 | series.put(LINE_COVERAGE, coverageBuildAction.getLineCoverage().getRoundedPercentage());
24 | series.put(BRANCH_COVERAGE, coverageBuildAction.getBranchCoverage().getRoundedPercentage());
25 |
26 | return series;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/Security2376Test/jobs/fs/builds/1/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | admin
9 |
10 | 1
11 |
12 |
13 |
14 |
15 | 2
16 | 1627289739464
17 | 1627289739514
18 | SUCCESS
19 | 52
20 | UTF-8
21 | false
22 |
23 | /var/jenkins_home/workspace/fs
24 | 2.303
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.github/labels.yml:
--------------------------------------------------------------------------------
1 | - name: bug
2 | description: Bugs or performance problems
3 | color: CC0000
4 | - name: feature
5 | color: 1d76db
6 | description: New features
7 | - name: enhancement
8 | description: Enhancement of existing functionality
9 | color: 0366d6
10 | - name: breaking
11 | description: Breaking Changes
12 | color: e4b21d
13 | - name: tests
14 | description: Enhancement of tests
15 | color: 0e8a16
16 | - name: documentation
17 | description: Enhancement of documentation
18 | color: bfafea
19 | - name: internal
20 | description: Internal changes without user or API impact
21 | color: e6e6e6
22 | - name: dependencies
23 | description: Update of dependencies
24 | color: e6e6e6
25 | - name: java
26 | description: Pull requests that update Maven Java dependencies
27 | color: b6b6b6
28 | - name: javascript
29 | description: Pull requests that update Node Javascript dependencies
30 | color: b6b6b6
31 | - name: github_actions
32 | description: Pull requests that update Github Actions workflows
33 | color: 909090
34 | - name: hacktoberfest
35 | description: Pull requests that participate in Hacktoberfest
36 | color: 7057ff
37 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name-template: 'v$RESOLVED_VERSION 🎁'
2 | tag-template: 'v$RESOLVED_VERSION'
3 |
4 | template: |
5 | $CHANGES
6 |
7 | # Emoji reference: https://gitmoji.carloscuesta.me/
8 | categories:
9 | - title: 💥 Breaking Changes
10 | label: breaking
11 | - title: 🚀 New Features
12 | label: feature
13 | - title: ✨ Improvements
14 | label: enhancement
15 | - title: 🐛 Bug Fixes
16 | label: bug
17 | - title: 📝 Documentation
18 | label: documentation
19 | - title: 📦 Dependency Updates
20 | label: dependencies
21 | - title: 🔧 Internal Changes
22 | label: internal
23 | - title: 🚦 Tests
24 | label: tests
25 |
26 | version-resolver:
27 | major:
28 | labels:
29 | - 'removed'
30 | minor:
31 | labels:
32 | - 'feature'
33 | - 'enhancement'
34 | - 'deprecated'
35 | - 'dependencies'
36 | - 'documentation'
37 | - 'tests'
38 | - 'internal'
39 | patch:
40 | labels:
41 | - 'bug'
42 | default: minor
43 |
44 | replacers:
45 | - search: '/\[*JENKINS-(\d+)\]*\s*-*\s*/g'
46 | replace: '[JENKINS-$1](https://issues.jenkins.io/browse/JENKINS-$1) - '
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shenyu Zheng and other Jenkins contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result:
--------------------------------------------------------------------------------
1 | #### Summary for modified lines
2 |
3 | - 3 lines have been modified
4 | - 2 lines are not covered
5 | - 1 line is covered only partially
6 |
7 | #### Overview by baseline
8 |
9 | * **[Overall project (difference to reference job)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#overview)**
10 | * Line Coverage: 91.02% (294/323) - Delta: +50.00%
11 | * Branch Coverage: 93.97% (109/116)
12 | * Complexity Density: 0.50%
13 | * Lines of Code: 323
14 | * **[Modified files (difference to overall project)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#modifiedFilesCoverage)**
15 | * Line Coverage: 50.00% (1/2) - Delta: +50.00%
16 | * **[Modified code lines (difference to modified files)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#modifiedLinesCoverage)**
17 | * Line Coverage: 50.00% (1/2) - Delta: +50.00%
18 | * **[Indirect changes](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#indirectCoverage)**
19 | * Line Coverage: 50.00% (1/2)
20 |
21 | #### Quality Gates Summary
22 |
23 | No active quality gates.
24 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import nl.jqno.equalsverifier.EqualsVerifier;
6 |
7 | import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder;
8 |
9 | import static io.jenkins.plugins.coverage.metrics.Assertions.*;
10 |
11 | /**
12 | * Tests the class {@link CoverageLeaf}.
13 | *
14 | * @author Ullrich Hafner
15 | */
16 | class CoverageLeafTest extends AbstractCoverageTest {
17 | private static final Coverage COVERED = new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build();
18 |
19 | @Test
20 | void shouldCreateLeaf() {
21 | CoverageLeaf coverageLeaf = new CoverageLeaf(LINE, COVERED);
22 |
23 | assertThat(coverageLeaf).hasMetric(LINE).hasToString("[Line]: 100.00% (1/1)");
24 | assertThat(coverageLeaf.getCoverage(LINE)).isEqualTo(COVERED);
25 | assertThat(coverageLeaf.getCoverage(CoverageMetric.MODULE)).isEqualTo(CoverageBuilder.NO_COVERAGE);
26 | }
27 |
28 | @Test
29 | void shouldObeyEqualsContract() {
30 | EqualsVerifier.forClass(CoverageLeaf.class).verify();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/etc/assertj-templates/has_assertion_template.txt:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Verifies that the actual ${class_to_assert}'s ${property} is equal to the given one.
4 | * @param ${property_safe} the given ${property} to compare the actual ${class_to_assert}'s ${property} to.
5 | * @return this assertion object.
6 | * @throws AssertionError - if the actual ${class_to_assert}'s ${property} is not equal to the given one.${throws_javadoc}
7 | */
8 | public ${self_type} has${Property}(${propertyType} ${property_safe}) ${throws}{
9 | // check that actual ${class_to_assert} we want to make assertions on is not null.
10 | isNotNull();
11 |
12 | // overrides the default error message with a more explicit one
13 | String assertjErrorMessage = "\nExpecting ${property} of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";
14 |
15 | // null safe check
16 | ${propertyType} actual${Property} = actual.${getter}();
17 | if (!java.util.Objects.deepEquals(actual${Property}, ${property_safe})) {
18 | failWithMessage(assertjErrorMessage, actual, ${property_safe}, actual${Property});
19 | }
20 |
21 | // return the current assertion for method chaining
22 | return ${myself};
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: 'GitHub CI'
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - main
8 | pull_request:
9 |
10 | jobs:
11 | build:
12 |
13 | strategy:
14 | matrix:
15 | platform: [ubuntu-latest, macos-latest, windows-latest]
16 | jdk: [17]
17 | include:
18 | - platform: ubuntu-latest
19 | jdk: 11
20 | - platform: macos-latest
21 | jdk: 11
22 |
23 | runs-on: ${{ matrix.platform }}
24 | name: on ${{ matrix.platform }} with JDK ${{ matrix.jdk }}
25 |
26 | steps:
27 | - uses: actions/checkout@v4
28 | - name: Set up Maven
29 | uses: stCarolas/setup-maven@v4.5
30 | with:
31 | maven-version: 3.9.1
32 | - name: Set up JDK ${{ matrix.jdk }}
33 | uses: actions/setup-java@v3
34 | with:
35 | distribution: 'temurin'
36 | java-version: '${{ matrix.jdk }}'
37 | check-latest: true
38 | cache: 'maven'
39 | - name: Build with Maven
40 | env:
41 | BROWSER: chrome-container
42 | run: mvn -V --color always -ntp clean verify --file pom.xml '-Djenkins.test.timeout=5000' '-Dgpg.skip'
43 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/JSONCoverageReportAdapter.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import io.jenkins.plugins.coverage.adapter.converter.JSONDocumentConverter;
5 | import io.jenkins.plugins.coverage.exception.CoverageException;
6 | import org.w3c.dom.Document;
7 |
8 | import java.io.File;
9 | import java.io.IOException;
10 |
11 | public abstract class JSONCoverageReportAdapter extends CoverageReportAdapter {
12 |
13 | /**
14 | * @param path Ant-style path of report files.
15 | */
16 | public JSONCoverageReportAdapter(String path) {
17 | super(path);
18 | }
19 |
20 | @Override
21 | protected Document convert(File source) throws CoverageException {
22 | try {
23 | return getConverter().convert(new ObjectMapper().readTree(source));
24 | } catch (IOException e) {
25 | e.printStackTrace();
26 | throw new CoverageException(e);
27 | }
28 | }
29 |
30 |
31 | /**
32 | * @return converter which convert JSONObject to DOM Document.
33 | */
34 | protected abstract JSONDocumentConverter getConverter();
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/CoverageElementInitializer.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage;
2 |
3 | import hudson.DescriptorExtensionList;
4 | import hudson.init.InitMilestone;
5 | import hudson.init.Initializer;
6 |
7 | import io.jenkins.plugins.coverage.adapter.CoverageAdapter;
8 | import io.jenkins.plugins.coverage.adapter.CoverageAdapterDescriptor;
9 | import io.jenkins.plugins.coverage.adapter.CoverageReportAdapterDescriptor;
10 | import io.jenkins.plugins.coverage.targets.CoverageElementRegister;
11 |
12 | @SuppressWarnings("unchecked")
13 | public class CoverageElementInitializer {
14 |
15 | @Initializer(after = InitMilestone.PLUGINS_STARTED)
16 | @SuppressWarnings("unused")
17 | public static void registerAllCoverageElement() {
18 | DescriptorExtensionList> reportDescriptors = CoverageAdapterDescriptor.all();
19 |
20 | reportDescriptors.stream()
21 | .filter(d -> d instanceof CoverageReportAdapterDescriptor)
22 | .map(d -> (CoverageReportAdapterDescriptor) d)
23 | .forEach(d -> CoverageElementRegister.addCoverageElements(d.getCoverageElementType(), d.getCoverageElements()));
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsId.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import java.util.Arrays;
4 | import java.util.Set;
5 | import java.util.stream.Collectors;
6 |
7 | /**
8 | * Contains color IDs which represent the keys of a JSON object that is dynamically filled with the currently set
9 | * Jenkins colors.
10 | *
11 | * @author Florian Orendi
12 | */
13 | public enum CoverageColorJenkinsId {
14 |
15 | GREEN("--green"),
16 | LIGHT_GREEN("--light-green"),
17 | YELLOW("--yellow"),
18 | LIGHT_YELLOW("--light-yellow"),
19 | ORANGE("--orange"),
20 | LIGHT_ORANGE("--light-orange"),
21 | RED("--red"),
22 | LIGHT_RED("--light-red");
23 |
24 | private final String jenkinsColorId;
25 |
26 | CoverageColorJenkinsId(final String colorId) {
27 | this.jenkinsColorId = colorId;
28 | }
29 |
30 | public String getJenkinsColorId() {
31 | return jenkinsColorId;
32 | }
33 |
34 | public static Set getAll() {
35 | return Arrays.stream(values())
36 | .map(id -> id.jenkinsColorId)
37 | .collect(Collectors.toSet());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/CoverageAggregationRule.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.targets;
2 |
3 |
4 | import java.util.Map;
5 | import java.util.TreeMap;
6 |
7 |
8 | public class CoverageAggregationRule {
9 |
10 | public static Map aggregate(CoverageElement source,
11 | CoverageElement input,
12 | Ratio inputResult,
13 | Map runningTotal) {
14 | Map result = new TreeMap<>(runningTotal);
15 |
16 | Ratio prevTotal = result.get(input);
17 | if (prevTotal == null) {
18 | prevTotal = Ratio.create(0, 0);
19 | }
20 |
21 | Ratio r = Ratio.create(inputResult.numerator + prevTotal.numerator, inputResult.denominator + prevTotal.denominator);
22 | result.put(input, r);
23 |
24 | return result;
25 | }
26 |
27 |
28 | public static Ratio combine(CoverageElement element, Ratio existingResult, Ratio additionalResult) {
29 | return Ratio.create(existingResult.numerator + additionalResult.numerator, existingResult.denominator + additionalResult.denominator);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/JavaCoverageReportAdapterDescriptor.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 | import java.util.List;
4 |
5 | import com.google.common.collect.Lists;
6 |
7 | import io.jenkins.plugins.coverage.targets.CoverageElement;
8 |
9 | public class JavaCoverageReportAdapterDescriptor extends CoverageReportAdapterDescriptor {
10 | /** A Java package. */
11 | public static final CoverageElement PACKAGE = new CoverageElement("Package", 1);
12 | /** A Java class. */
13 | public static final CoverageElement CLASS = new CoverageElement("Class", 3);
14 | /** A Java method. */
15 | public static final CoverageElement METHOD = new CoverageElement("Method", 4);
16 |
17 | public JavaCoverageReportAdapterDescriptor(final Class extends CoverageReportAdapter> clazz) {
18 | super(clazz);
19 | }
20 |
21 | @Override
22 | public List getCoverageElements() {
23 | return Lists.newArrayList(new CoverageElement("Group", 0),
24 | PACKAGE,
25 | CoverageElement.FILE,
26 | CLASS,
27 | METHOD);
28 | }
29 |
30 | @Override
31 | public String getCoverageElementType() {
32 | return CoverageElement.COVERAGE_ELEMENT_TYPE_JAVA;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/CoverageTrendTree.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.targets;
2 |
3 | import org.kohsuke.stapler.export.Exported;
4 | import org.kohsuke.stapler.export.ExportedBean;
5 |
6 | import java.util.LinkedList;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | @ExportedBean
11 | public class CoverageTrendTree {
12 |
13 | private String name;
14 | private List trends;
15 | private Map children;
16 |
17 | public CoverageTrendTree(String name, List trends, Map children) {
18 | this.name = name;
19 | this.trends = trends;
20 | this.children = children;
21 | }
22 |
23 | @Exported
24 | public String getName() {
25 | return name;
26 | }
27 |
28 | @Exported
29 | public List getTrends() {
30 | return trends;
31 | }
32 |
33 | @Exported
34 | public List getChildren() {
35 | List childrenTrends = new LinkedList<>();
36 |
37 | for (Map.Entry e : children.entrySet()) {
38 | childrenTrends.add(new CoverageTrendTree(e.getKey(), e.getValue().getCoverageTrends(), e.getValue().getChildrenReal()));
39 | }
40 | return childrenTrends;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-quality-gate.checks-expected-result:
--------------------------------------------------------------------------------
1 | #### Quality Gates Summary
2 |
3 | Overall result: Failed
4 | - Overall project - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00)
5 | - Overall project - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00)
6 | - Modified code lines - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00)
7 | - Modified code lines - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00)
8 | - Modified files - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00)
9 | - Modified files - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00)
10 | - Overall project (difference to reference job) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00)
11 | - Overall project (difference to reference job) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00)
12 | - Modified code lines (difference to modified files) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00)
13 | - Modified code lines (difference to modified files) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00)
14 | - Modified files (difference to overall project) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00)
15 | - Modified files (difference to overall project) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00)
16 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-lower-coverage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | /Users/leobalter/dev/testing/solutions/3
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/cobertura-lower-coverage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | /Users/leobalter/dev/testing/solutions/3
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendencyTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.assertj.core.api.Assertions.*;
6 |
7 | /**
8 | * Test class for {@link CoverageChangeTendency}.
9 | *
10 | * @author Florian Orendi
11 | */
12 | class CoverageChangeTendencyTest {
13 |
14 | private static final ColorProvider COLOR_PROVIDER = ColorProviderFactory.createDefaultColorProvider();
15 |
16 | @Test
17 | void shouldGetDisplayColorsForTendency() {
18 | assertThat(CoverageChangeTendency.getDisplayColorsForTendency(1.0, COLOR_PROVIDER))
19 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.EXCELLENT));
20 | assertThat(CoverageChangeTendency.getDisplayColorsForTendency(0.0, COLOR_PROVIDER))
21 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.AVERAGE));
22 | assertThat(CoverageChangeTendency.getDisplayColorsForTendency(-1.0, COLOR_PROVIDER))
23 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.INSUFFICIENT));
24 | assertThat(CoverageChangeTendency.getDisplayColorsForTendency(null, COLOR_PROVIDER))
25 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.WHITE));
26 | }
27 |
28 | @Test
29 | void shouldGetColorizationId() {
30 | assertThat(CoverageChangeTendency.INCREASED.getColorizationId()).isEqualTo(ColorId.EXCELLENT);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/SourceViewModel/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
22 |
23 |
24 |
25 | ${%Source code is unavailable.}
26 | ${%Some possible reasons are:}
27 |
28 | ${%reason.1}
29 | ${%reason.2}
30 | ${%reason.3}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/model/AcuCobolParser.java.txt:
--------------------------------------------------------------------------------
1 | package edu.hm.hafner.analysis.parser;
2 |
3 | import java.util.Optional;
4 | import java.util.regex.Matcher;
5 |
6 | import edu.hm.hafner.analysis.Issue;
7 | import edu.hm.hafner.analysis.IssueBuilder;
8 | import edu.hm.hafner.analysis.LookaheadParser;
9 | import edu.hm.hafner.util.LookaheadStream;
10 |
11 | import static edu.hm.hafner.analysis.Categories.*;
12 |
13 | /**
14 | * A parser for the Acu Cobol compile.
15 | *
16 | * @author jerryshea
17 | */
18 | public class AcuCobolParser extends LookaheadParser {
19 | private static final long serialVersionUID = -894639209290549425L;
20 |
21 | private static final String ACU_COBOL_WARNING_PATTERN = "^\\s*(\\[.*\\])?\\s*?(.*), line ([0-9]*): Warning: (.*)$";
22 |
23 | /**
24 | * Creates a new instance of {@link AcuCobolParser}.
25 | */
26 | public AcuCobolParser() {
27 | super(ACU_COBOL_WARNING_PATTERN);
28 | }
29 |
30 | @Override
31 | protected boolean isLineInteresting(final String line) {
32 | return line.contains("Warning");
33 | }
34 |
35 | @Override
36 | protected Optional createIssue(final Matcher matcher, final LookaheadStream lookahead,
37 | final IssueBuilder builder) {
38 | return builder.setFileName(matcher.group(2))
39 | .setLineStart(matcher.group(3))
40 | .setCategory(guessCategory(matcher.group(4)))
41 | .setMessage(matcher.group(4))
42 | .buildOptional();
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/source/AcuCobolParser.java.txt:
--------------------------------------------------------------------------------
1 | package edu.hm.hafner.analysis.parser;
2 |
3 | import java.util.Optional;
4 | import java.util.regex.Matcher;
5 |
6 | import edu.hm.hafner.analysis.Issue;
7 | import edu.hm.hafner.analysis.IssueBuilder;
8 | import edu.hm.hafner.analysis.LookaheadParser;
9 | import edu.hm.hafner.util.LookaheadStream;
10 |
11 | import static edu.hm.hafner.analysis.Categories.*;
12 |
13 | /**
14 | * A parser for the Acu Cobol compile.
15 | *
16 | * @author jerryshea
17 | */
18 | public class AcuCobolParser extends LookaheadParser {
19 | private static final long serialVersionUID = -894639209290549425L;
20 |
21 | private static final String ACU_COBOL_WARNING_PATTERN = "^\\s*(\\[.*\\])?\\s*?(.*), line ([0-9]*): Warning: (.*)$";
22 |
23 | /**
24 | * Creates a new instance of {@link AcuCobolParser}.
25 | */
26 | public AcuCobolParser() {
27 | super(ACU_COBOL_WARNING_PATTERN);
28 | }
29 |
30 | @Override
31 | protected boolean isLineInteresting(final String line) {
32 | return line.contains("Warning");
33 | }
34 |
35 | @Override
36 | protected Optional createIssue(final Matcher matcher, final LookaheadStream lookahead,
37 | final IssueBuilder builder) {
38 | return builder.setFileName(matcher.group(2))
39 | .setLineStart(matcher.group(3))
40 | .setCategory(guessCategory(matcher.group(4)))
41 | .setMessage(matcher.group(4))
42 | .buildOptional();
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/etc/Jenkinsfile.analysis:
--------------------------------------------------------------------------------
1 | node('java-agent') {
2 | stage ('Checkout') {
3 | checkout scm
4 | }
5 |
6 | stage ('Git mining') {
7 | discoverGitReferenceBuild()
8 | mineRepository()
9 | gitDiffStat()
10 | }
11 |
12 | stage ('Build, Test, and Static Analysis') {
13 | withMaven(mavenLocalRepo: '/var/data/m2repository', mavenOpts: '-Xmx768m -Xms512m') {
14 | sh 'mvn -V -e clean verify -Dmaven.test.failure.ignore -Dgpg.skip'
15 | }
16 |
17 | recordIssues tools: [java(), javaDoc()], aggregatingResults: 'true', id: 'java', name: 'Java', filters:[excludeFile('.*Assert.java')]
18 | recordIssues tool: errorProne(), healthy: 1, unhealthy: 20
19 |
20 | junit testResults: '**/target/*-reports/TEST-*.xml'
21 | recordCoverage tools: [[parser: 'JACOCO', pattern: '**/jacoco/jacoco.xml']], sourceCodeRetention: 'EVERY_BUILD',
22 | qualityGates: [ [threshold: 90.0, metric: 'LINE', baseline: 'PROJECT', criticality: 'UNSTABLE']],
23 | sourceDirectories: [[path: 'plugin/src/main/java']]
24 | recordIssues tools: [checkStyle(pattern: 'target/checkstyle-result.xml'),
25 | spotBugs(pattern: 'target/spotbugsXml.xml'),
26 | pmdParser(pattern: 'target/pmd.xml'),
27 | cpd(pattern: 'target/cpd.xml'),
28 | revApi(pattern: 'target/revapi-result.json'),
29 | taskScanner(highTags:'FIXME', normalTags:'TODO', includePattern: '**/*.java', excludePattern: 'target/**/*')]
30 | }
31 |
32 | stage ('Collect Maven Warnings') {
33 | recordIssues tool: mavenConsole()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoveragePublisher/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/HasName.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2007-2018 manolo, Seiji Sogabe, Michael Barrientos and Jenkins contributors
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 | package io.jenkins.plugins.coverage.targets;
23 |
24 | // Code adopted from Cobertura Plugin https://github.com/jenkinsci/cobertura-plugin/
25 | /**
26 | * Interface for elements which have a display name
27 | *
28 | * @author manolo
29 | * @since 20-Mar-2010 08:59:50
30 | */
31 | public interface HasName {
32 |
33 | public String getName();
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/Chartable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2007-2018 Michael Barrientos, Seiji Sogabe and Jenkins contributors
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 | package io.jenkins.plugins.coverage.targets;
23 |
24 | import hudson.model.Run;
25 |
26 |
27 | import java.util.Map;
28 |
29 | // Code adopted from Cobertura Plugin https://github.com/jenkinsci/cobertura-plugin/
30 | public interface Chartable {
31 |
32 | Chartable getPreviousResult();
33 |
34 | Map getResults();
35 |
36 | Run, ?> getOwner();
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/column.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ${coverageText}
21 |
22 |
23 | ${coverageText}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | ${coverageText}
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-higher-coverage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | /Users/leobalter/dev/testing/solutions/3
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/cobertura-higher-coverage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | /Users/leobalter/dev/testing/solutions/3
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-package-root.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | /home/user/dev/python/package
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/detector/AntPathReportDetector.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.detector;
2 |
3 | import edu.umd.cs.findbugs.annotations.NonNull;
4 | import hudson.Extension;
5 | import hudson.FilePath;
6 | import hudson.model.Run;
7 | import hudson.model.TaskListener;
8 | import io.jenkins.plugins.coverage.adapter.CoverageAdapterDescriptor;
9 | import io.jenkins.plugins.coverage.exception.CoverageException;
10 | import org.jenkinsci.Symbol;
11 | import org.kohsuke.stapler.DataBoundConstructor;
12 |
13 | import java.io.IOException;
14 | import java.util.Arrays;
15 | import java.util.List;
16 |
17 | public class AntPathReportDetector extends ReportDetector {
18 |
19 | private String path;
20 |
21 | @DataBoundConstructor
22 | public AntPathReportDetector(String path) {
23 | this.path = path;
24 | }
25 |
26 | @Override
27 | public List findFiles(Run, ?> run, FilePath workspace, TaskListener listener) throws CoverageException {
28 | try {
29 | return Arrays.asList(workspace.list(path));
30 | } catch (IOException | InterruptedException e) {
31 | throw new CoverageException(e);
32 | }
33 | }
34 |
35 | public String getPath() {
36 | return path;
37 | }
38 |
39 | @Symbol("antPath")
40 | @Extension
41 | public static final class AntPathReportDetectorDescriptor extends CoverageAdapterDescriptor {
42 |
43 | public AntPathReportDetectorDescriptor() {
44 | super(AntPathReportDetector.class);
45 | }
46 |
47 | @NonNull
48 | @Override
49 | public String getDisplayName() {
50 | return Messages.AntPathDetector_displayName();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Locale;
4 | import java.util.Optional;
5 |
6 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
7 | import io.jenkins.plugins.coverage.model.CoverageMetric;
8 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
9 | import io.jenkins.plugins.coverage.model.Messages;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel;
12 |
13 | /**
14 | * Concrete implementation of {@link CoverageColumnType} which represents the project coverage.
15 | *
16 | * @author Florian Orendi
17 | */
18 | public class ProjectCoverage extends CoverageColumnType {
19 |
20 | /**
21 | * Creates a column type to be used for representing the project coverage.
22 | */
23 | public ProjectCoverage() {
24 | super(Messages._Project_Coverage_Type());
25 | }
26 |
27 | @Override
28 | public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) {
29 | if (action.hasCoverage(metric)) {
30 | return Optional.of(action.getCoverage(metric).getCoveredPercentage());
31 | }
32 | return Optional.empty();
33 | }
34 |
35 | @Override
36 | public DisplayColors getDisplayColors(final CoveragePercentage coverage) {
37 | return CoverageLevel.getDisplayColorsOfCoverageLevel(coverage.getDoubleValue(), getColorProvider());
38 | }
39 |
40 | @Override
41 | public String formatCoverage(final CoveragePercentage coverage, final Locale locale) {
42 | return coverage.formatPercentage(locale);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Locale;
4 | import java.util.Optional;
5 |
6 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
7 | import io.jenkins.plugins.coverage.model.CoverageMetric;
8 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
9 | import io.jenkins.plugins.coverage.model.Messages;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageChangeTendency;
12 |
13 | /**
14 | * Concrete implementation of {@link CoverageColumnType} which represents the project coverage delta.
15 | *
16 | * @author Florian Orendi
17 | */
18 | public class ProjectCoverageDelta extends CoverageColumnType {
19 |
20 | /**
21 | * Creates a column type to be used for representing the project coverage delta.
22 | */
23 | public ProjectCoverageDelta() {
24 | super(Messages._Project_Coverage_Delta_Type());
25 | }
26 |
27 | @Override
28 | public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) {
29 | if (action.hasDelta(metric)) {
30 | return Optional.of(action.getDifference().get(metric));
31 | }
32 | return Optional.empty();
33 | }
34 |
35 | @Override
36 | public DisplayColors getDisplayColors(final CoveragePercentage coverage) {
37 | return CoverageChangeTendency.getDisplayColorsForTendency(coverage.getDoubleValue(), getColorProvider());
38 | }
39 |
40 | @Override
41 | public String formatCoverage(final CoveragePercentage coverage, final Locale locale) {
42 | return coverage.formatDeltaPercentage(locale);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/SafeFractionTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import org.apache.commons.lang3.math.Fraction;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static org.assertj.core.api.Assertions.*;
7 |
8 | /**
9 | * Tests the class {@link SafeFraction}.
10 | *
11 | * @author Ullrich Hafner
12 | */
13 | class SafeFractionTest {
14 | @Test
15 | void shouldDelegateToFraction() {
16 | Fraction ten = Fraction.getFraction(10, 1);
17 | SafeFraction safeFraction = new SafeFraction(ten);
18 | assertThat(safeFraction.multiplyBy(ten).doubleValue()).isEqualTo(100.0);
19 | assertThat(safeFraction.subtract(ten).doubleValue()).isEqualTo(0);
20 | assertThat(safeFraction.add(ten).doubleValue()).isEqualTo(20.0);
21 | }
22 |
23 | @Test
24 | void shouldHandleOverflowForMultiply() {
25 | Fraction fraction = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1);
26 | SafeFraction safeFraction = new SafeFraction(fraction);
27 | assertThat(safeFraction.multiplyBy(Fraction.getFraction("100.0")).doubleValue()).isEqualTo(100.0);
28 | }
29 |
30 | @Test
31 | void shouldHandleOverflowForSubtract() {
32 | Fraction fraction = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1);
33 | SafeFraction safeFraction = new SafeFraction(fraction);
34 | assertThat(safeFraction.subtract(Fraction.getFraction("100.0")).doubleValue()).isEqualTo(-99.0);
35 | }
36 |
37 | @Test
38 | void shouldHandleOverflowForAdd() {
39 | Fraction fraction = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1);
40 | SafeFraction safeFraction = new SafeFraction(fraction);
41 | assertThat(safeFraction.add(Fraction.getFraction("100.0")).doubleValue()).isEqualTo(101.0);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
6 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
7 | import hudson.model.Run;
8 |
9 | import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite;
10 |
11 | import static org.assertj.core.api.Assertions.*;
12 |
13 | /**
14 | * Integration test to check if declarative pipelines are supported.
15 | */
16 | class DeclarativePipelineSupportITest extends IntegrationTestWithJenkinsPerSuite {
17 | private static final String JACOCO_ANALYSIS_MODEL_XML = "jacoco-analysis-model.xml";
18 |
19 | /**
20 | * Check if code coverage is supported in declarative pipelines.
21 | */
22 | @Test
23 | void declarativePipelineSupportJacoco() {
24 | WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_XML);
25 |
26 | job.setDefinition(new CpsFlowDefinition("pipeline {\n"
27 | + " agent any\n"
28 | + " stages {\n"
29 | + " stage('Test') {\n"
30 | + " steps {\n"
31 | + " publishCoverage(\n"
32 | + " adapters: [\n"
33 | + " jacocoAdapter('" + JACOCO_ANALYSIS_MODEL_XML + "')\n"
34 | + " ],\n"
35 | + " )}\n"
36 | + " }\n"
37 | + " }\n"
38 | + "}", true));
39 | Run, ?> build = buildSuccessfully(job);
40 | assertThat(build.getAction(CoverageBuildAction.class).getLineCoverage())
41 | .isEqualTo(new Coverage.CoverageBuilder().setCovered(6083).setMissed(6368 - 6083).build());
42 | }
43 | }
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendency.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import edu.umd.cs.findbugs.annotations.NonNull;
4 |
5 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
6 |
7 | /**
8 | * Provides the colorization for different coverage change tendencies.
9 | *
10 | * @author Florian Orendi
11 | */
12 | public enum CoverageChangeTendency {
13 |
14 | INCREASED(ColorId.EXCELLENT),
15 | EQUALS(ColorId.AVERAGE),
16 | DECREASED(ColorId.INSUFFICIENT),
17 | NA(ColorId.WHITE);
18 |
19 | private final ColorId colorizationId;
20 |
21 | CoverageChangeTendency(final ColorId colorizationId) {
22 | this.colorizationId = colorizationId;
23 | }
24 |
25 | /**
26 | * Provides the {@link DisplayColors display colors} which match with the passed coverage change tendency.
27 | *
28 | * @param change
29 | * The coverage change
30 | * @param colorProvider
31 | * The {@link ColorProvider color provider} to be used
32 | *
33 | * @return the matching change level
34 | */
35 | public static DisplayColors getDisplayColorsForTendency(final Double change,
36 | @NonNull final ColorProvider colorProvider) {
37 | ColorId colorId;
38 | if (change == null || change.isNaN()) {
39 | colorId = NA.colorizationId;
40 | }
41 | else if (change > 0) {
42 | colorId = INCREASED.colorizationId;
43 | }
44 | else if (change < 0) {
45 | colorId = DECREASED.colorizationId;
46 | }
47 | else {
48 | colorId = EQUALS.colorizationId;
49 | }
50 | return colorProvider.getDisplayColorsOf(colorId);
51 | }
52 |
53 | public ColorId getColorizationId() {
54 | return colorizationId;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Locale;
4 | import java.util.Optional;
5 |
6 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
7 | import io.jenkins.plugins.coverage.model.CoverageMetric;
8 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
9 | import io.jenkins.plugins.coverage.model.Messages;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel;
12 |
13 | /**
14 | * Concrete implementation of {@link CoverageColumnType} which represents the change coverage.
15 | *
16 | * @author Florian Orendi
17 | */
18 | public class ChangeCoverage extends CoverageColumnType {
19 |
20 | /**
21 | * Creates a column type to be used for representing the change coverage.
22 | */
23 | public ChangeCoverage() {
24 | super(Messages._Change_Coverage_Type());
25 | }
26 |
27 | @Override
28 | public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) {
29 | if (action.hasChangeCoverage(metric)) {
30 | return Optional.of(action.getChangeCoverage(metric).getCoveredPercentage());
31 | }
32 | return Optional.empty();
33 | }
34 |
35 | @Override
36 | public DisplayColors getDisplayColors(final CoveragePercentage coverage) {
37 | return CoverageLevel.getDisplayColorsOfCoverageLevel(coverage.getDoubleValue(), getColorProvider());
38 | }
39 |
40 | @Override
41 | public String formatCoverage(final CoveragePercentage coverage, final Locale locale) {
42 | return coverage.formatPercentage(locale);
43 | }
44 |
45 | @Override
46 | public String getAnchor() {
47 | return "#changeCoverage";
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/webapp/css/style.css:
--------------------------------------------------------------------------------
1 | table.source {
2 | border-spacing: 0;
3 | border-collapse: collapse;
4 | border: 1px solid #bbb;
5 | width: 100%;
6 | }
7 |
8 | table.source td {
9 | border-spacing: 0;
10 | border-collapse: collapse;
11 | border: 0 solid #bbb;
12 | padding-left: 0.2em;
13 | padding-right: 0.2em;
14 | font-family: monospace;
15 | }
16 |
17 | table.source .text {
18 | margin-top: -1px;
19 | }
20 |
21 | table.source td.line {
22 | text-align: right;
23 | border-width: 0 1px 0 0;
24 | }
25 |
26 | table.source td.hits {
27 | text-align: right;
28 | border-width: 0px 1px 0px 0px;
29 | }
30 |
31 | table.source th {
32 | padding-left: 0.5em;
33 | font-weight: bold;
34 | font-family: serif;
35 | background-color: #f0f0f0;
36 | border-width: 1px 1px 1px 1px;
37 | text-align: left;
38 | }
39 |
40 | table.source tr.coverFull {
41 | background-color: #dfd;
42 | }
43 |
44 | table.source tr.coverPart {
45 | background-color: #ff9;
46 | }
47 |
48 | table.source tr.coverPart td.hits, table.source tr.coverNone td.hits {
49 | font-weight: bold;
50 | }
51 |
52 | table.source tr.coverNone {
53 | background-color: #fdd;
54 | }
55 |
56 | table.source tr.noCover {
57 | background-color: #fff;
58 | }
59 |
60 | table.source tr.coverSkip {
61 | background-color: #b4b4b4;
62 | }
63 |
64 | .center {
65 | text-align: center;
66 | }
67 |
68 | /* Bar graph in table */
69 | .bar-graph {
70 | float: left;
71 | color: transparent;
72 | }
73 |
74 | .missed {
75 | background-color: #FFCDD2;
76 | }
77 |
78 | .missed--hover:hover {
79 | background-color: #EF9A9A;
80 | }
81 |
82 | .covered {
83 | background-color: #C8E6C9;
84 | }
85 |
86 | .covered--hover:hover {
87 | background-color: #A5D6A7;
88 | }
89 |
90 | /* Table Delta Badge */
91 | .badge-delta {
92 | width: 50px !important;
93 | vertical-align: text-top !important;
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorPalette.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import java.awt.*;
4 |
5 | /**
6 | * Provides a color palette which can be used as a plugin internal fallback if no other color schemes have been defined.
7 | * The defined colors correspond to the Jenkins Design
8 | * Library .
9 | *
10 | * @author Florian Orendi
11 | */
12 | public enum CoverageColorPalette {
13 |
14 | WHITE(ColorId.WHITE, new Color(255, 255, 255), new Color(0, 0, 0)),
15 | BLACK(ColorId.BLACK, new Color(0, 0, 0), new Color(255, 255, 255)),
16 |
17 | RED(ColorId.INSUFFICIENT, new Color(230, 0, 31), new Color(255, 255, 255)),
18 | LIGHT_RED(ColorId.VERY_BAD, new Color(255, 77, 101), new Color(255, 255, 255)),
19 |
20 | ORANGE(ColorId.BAD, new Color(254, 130, 10), new Color(0, 0, 0)),
21 | LIGHT_ORANGE(ColorId.INADEQUATE, new Color(254, 182, 112), new Color(0, 0, 0)),
22 |
23 | YELLOW(ColorId.AVERAGE, new Color(255, 204, 0), new Color(0, 0, 0)),
24 | LIGHT_YELLOW(ColorId.GOOD, new Color(255, 224, 102), new Color(0, 0, 0)),
25 |
26 | LIGHT_GREEN(ColorId.VERY_GOOD, new Color(75, 223, 124), new Color(0, 0, 0)),
27 | GREEN(ColorId.EXCELLENT, new Color(30, 166, 75), new Color(255, 255, 255));
28 |
29 | private final ColorId colorId;
30 | private final Color fillColor;
31 | private final Color lineColor;
32 |
33 | CoverageColorPalette(final ColorId colorId, final Color fillColor, final Color lineColor) {
34 | this.colorId = colorId;
35 | this.fillColor = fillColor;
36 | this.lineColor = lineColor;
37 | }
38 |
39 | public ColorId getColorId() {
40 | return colorId;
41 | }
42 |
43 | public Color getFillColor() {
44 | return fillColor;
45 | }
46 |
47 | public Color getLineColor() {
48 | return lineColor;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/job-dsl.yaml:
--------------------------------------------------------------------------------
1 | jobs:
2 | - script: >
3 | freeStyleJob('dsl-freestyle-job') {
4 | publishers {
5 | recordCoverage {
6 | tools {
7 | coverageTool {
8 | parser('JACOCO')
9 | pattern('jacoco-pattern.*')
10 | }
11 | coverageTool {
12 | parser('COBERTURA')
13 | pattern('cobertura-pattern.*')
14 | }
15 | }
16 | qualityGates {
17 | coverageQualityGate {
18 | threshold(70)
19 | metric('LINE')
20 | baseline('PROJECT')
21 | criticality('UNSTABLE')
22 | }
23 | coverageQualityGate {
24 | threshold(80)
25 | metric('BRANCH')
26 | baseline('MODIFIED_LINES')
27 | criticality('FAILURE')
28 | }
29 | }
30 | id('my-coverage')
31 | name('My Coverage')
32 | enabledForFailure(true)
33 | skipPublishingChecks(true)
34 | failOnError(true)
35 | skipSymbolicLinks(true)
36 | scm('my-git')
37 | sourceCodeEncoding('UTF-8')
38 | sourceCodeRetention('EVERY_BUILD')
39 | sourceDirectories {
40 | sourceCodeDirectory {
41 | path('directory-1')
42 | }
43 | sourceCodeDirectory {
44 | path('directory-2')
45 | }
46 | }
47 | }
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Locale;
4 | import java.util.Optional;
5 |
6 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
7 | import io.jenkins.plugins.coverage.model.CoverageMetric;
8 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
9 | import io.jenkins.plugins.coverage.model.Messages;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageChangeTendency;
12 |
13 | /**
14 | * Concrete implementation of {@link CoverageColumnType} which represents the change coverage delta.
15 | *
16 | * @author Florian Orendi
17 | */
18 | public class ChangeCoverageDelta extends CoverageColumnType {
19 |
20 | /**
21 | * Creates a column type to be used for representing the change coverage delta.
22 | */
23 | public ChangeCoverageDelta() {
24 | super(Messages._Change_Coverage_Delta_Type());
25 | }
26 |
27 | @Override
28 | public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) {
29 | if (action.hasChangeCoverageDifference(metric)) {
30 | return Optional.of(action.getChangeCoverageDifference(metric));
31 | }
32 | return Optional.empty();
33 | }
34 |
35 | @Override
36 | public DisplayColors getDisplayColors(final CoveragePercentage coverage) {
37 | return CoverageChangeTendency.getDisplayColorsForTendency(coverage.getDoubleValue(), getColorProvider());
38 | }
39 |
40 | @Override
41 | public String formatCoverage(final CoveragePercentage coverage, final Locale locale) {
42 | return coverage.formatDeltaPercentage(locale);
43 | }
44 |
45 | @Override
46 | public String getAnchor() {
47 | return "#changeCoverage";
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/BuildUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | package io.jenkins.plugins.coverage;
24 |
25 | import hudson.model.Run;
26 |
27 |
28 | public final class BuildUtils {
29 |
30 | /**
31 | * BuildUtils cannot be instantiated.
32 | */
33 | private BuildUtils() {
34 | }
35 |
36 | /**
37 | * Get previous not failed completed build.
38 | *
39 | * @param b current build
40 | * @return previous not failed completed build
41 | */
42 | public static Run, ?> getPreviousNotFailedCompletedBuild(Run, ?> b) {
43 | while (true) {
44 | b = b.getPreviousNotFailedBuild();
45 | if (b == null) {
46 | return null;
47 | }
48 | if (!b.isBuilding()) {
49 | return b;
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/resources/io/jenkins/plugins/coverage/cobertura-coverage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | /Users/leobalter/dev/testing/solutions/3
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Locale;
4 | import java.util.Optional;
5 |
6 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
7 | import io.jenkins.plugins.coverage.model.CoverageMetric;
8 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
9 | import io.jenkins.plugins.coverage.model.Messages;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel;
12 |
13 | /**
14 | * Concrete implementation of {@link CoverageColumnType} which represents the indirect coverage changes.
15 | *
16 | * @author Florian Orendi
17 | */
18 | public class IndirectCoverageChanges extends CoverageColumnType {
19 |
20 | /**
21 | * Creates a column type to be used for representing the indirect coverage changes.
22 | */
23 | public IndirectCoverageChanges() {
24 | super(Messages._Indirect_Coverage_Changes_Type());
25 | }
26 |
27 | @Override
28 | public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) {
29 | if (action.hasIndirectCoverageChanges(metric)) {
30 | return Optional.of(action.getIndirectCoverageChanges(metric).getCoveredPercentage());
31 | }
32 | return Optional.empty();
33 | }
34 |
35 | @Override
36 | public DisplayColors getDisplayColors(final CoveragePercentage coverage) {
37 | return CoverageLevel.getDisplayColorsOfCoverageLevel(coverage.getDoubleValue(), getColorProvider());
38 | }
39 |
40 | @Override
41 | public String formatCoverage(final CoveragePercentage coverage, final Locale locale) {
42 | return coverage.formatPercentage(locale);
43 | }
44 |
45 | @Override
46 | public String getAnchor() {
47 | return "#indirectCoverage";
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/adapter/istanbul-cobertura-to-standard.xsl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | istanbul
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 |
6 | import org.apache.commons.lang3.exception.ExceptionUtils;
7 |
8 | import hudson.model.Run;
9 | import hudson.util.TextFile;
10 |
11 | import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade;
12 |
13 | /**
14 | * Server side model that provides the data for the source code view of the coverage results. The layout of the
15 | * associated view is defined corresponding jelly view 'index.jelly'.
16 | *
17 | * @author Ullrich Hafner
18 | */
19 | public class SourceViewModel extends CoverageViewModel {
20 | /**
21 | * Creates a new source view model instance.
22 | *
23 | * @param owner
24 | * the owner of this view
25 | * @param fileNode
26 | * the selected file node of the coverage tree
27 | */
28 | public SourceViewModel(final Run, ?> owner, final CoverageNode fileNode) {
29 | super(owner, fileNode);
30 | }
31 |
32 | /**
33 | * Returns the source file rendered in HTML.
34 | *
35 | * @return {@code true} if the source file is available, {@code false} otherwise
36 | */
37 | public String getSourceFileContent() {
38 | try {
39 | File rootDir = getOwner().getRootDir();
40 | if (isSourceFileInNewFormatAvailable(getNode())) {
41 | return new SourceCodeFacade().read(rootDir, getId(), getNode().getPath());
42 | }
43 | if (isSourceFileInOldFormatAvailable(getNode())) {
44 | return new TextFile(getFileForBuildsWithOldVersion(rootDir, getNode().getName())).read(); // fallback with sources persisted using the < 2.1.0 serialization
45 | }
46 | return Messages.Coverage_Not_Available();
47 | }
48 | catch (IOException | InterruptedException exception) {
49 | return ExceptionUtils.getStackTrace(exception);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageLevelTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import java.awt.*;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
8 |
9 | import static org.assertj.core.api.Assertions.*;
10 |
11 | /**
12 | * Test class for {@link CoverageLevel}.
13 | *
14 | * @author Florian Orendi
15 | */
16 | class CoverageLevelTest {
17 |
18 | private static final ColorProvider COLOR_PROVIDER = ColorProviderFactory.createDefaultColorProvider();
19 |
20 | @Test
21 | void shouldHaveWorkingGetters() {
22 | CoverageLevel coverageLevel = CoverageLevel.LVL_0;
23 | assertThat(coverageLevel.getLevel()).isEqualTo(0.0);
24 | assertThat(coverageLevel.getColorizationId()).isEqualTo(ColorId.INSUFFICIENT);
25 | }
26 |
27 | @Test
28 | void shouldGetDisplayColorsOfCoveragePercentage() {
29 | Color blendedColor = ColorProvider.blendColors(
30 | COLOR_PROVIDER.getDisplayColorsOf(CoverageLevel.LVL_60.getColorizationId()).getFillColor(),
31 | COLOR_PROVIDER.getDisplayColorsOf(CoverageLevel.LVL_70.getColorizationId()).getFillColor());
32 |
33 | assertThat(CoverageLevel.getDisplayColorsOfCoverageLevel(65.0, COLOR_PROVIDER))
34 | .isEqualTo(new DisplayColors(COLOR_PROVIDER.getDisplayColorsOf(ColorId.BLACK).getFillColor(),
35 | blendedColor));
36 | assertThat(CoverageLevel.getDisplayColorsOfCoverageLevel(96.0, COLOR_PROVIDER))
37 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.EXCELLENT));
38 | assertThat(CoverageLevel.getDisplayColorsOfCoverageLevel(50.0, COLOR_PROVIDER))
39 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.VERY_BAD));
40 | assertThat(CoverageLevel.getDisplayColorsOfCoverageLevel(-2.0, COLOR_PROVIDER))
41 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.WHITE));
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/MethodCoverageNode.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import java.util.Objects;
4 |
5 | /**
6 | * A {@link CoverageNode} for a specific method.
7 | *
8 | * @author Florian Orendi
9 | */
10 | public class MethodCoverageNode extends CoverageNode {
11 |
12 | private static final long serialVersionUID = -5765205034179396434L;
13 |
14 | /**
15 | * The line number where the code of method begins (not including the method head).
16 | */
17 | private final int lineNumber;
18 |
19 | /**
20 | * Creates a new coverage item node with the given name.
21 | *
22 | * @param name
23 | * The human-readable name of the node
24 | * @param lineNumber
25 | * The line number where the method begins (not including the method head)
26 | */
27 | public MethodCoverageNode(final String name, final int lineNumber) {
28 | super(CoverageMetric.METHOD, name);
29 | this.lineNumber = lineNumber;
30 | }
31 |
32 | /**
33 | * Checks whether the line number is valid.
34 | *
35 | * @return {@code true} if the line number is valid, else {@code false}
36 | */
37 | public boolean hasValidLineNumber() {
38 | return lineNumber > 0;
39 | }
40 |
41 | public int getLineNumber() {
42 | return lineNumber;
43 | }
44 |
45 | @Override
46 | protected CoverageNode copyEmpty() {
47 | return new MethodCoverageNode(getName(), getLineNumber());
48 | }
49 |
50 | @Override
51 | public boolean equals(final Object o) {
52 | if (this == o) {
53 | return true;
54 | }
55 | if (o == null || getClass() != o.getClass()) {
56 | return false;
57 | }
58 | if (!super.equals(o)) {
59 | return false;
60 | }
61 | MethodCoverageNode that = (MethodCoverageNode) o;
62 | return lineNumber == that.lineNumber;
63 | }
64 |
65 | @Override
66 | public int hashCode() {
67 | return Objects.hash(super.hashCode(), lineNumber);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/adapter/cobertura-to-standard.xsl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | cobertura
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Optional;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
8 | import io.jenkins.plugins.coverage.model.CoverageMetric;
9 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
12 |
13 | import static org.assertj.core.api.Assertions.*;
14 |
15 | /**
16 | * Test class for {@link ProjectCoverage}.
17 | *
18 | * @author Florian Orendi
19 | */
20 | class ChangeCoverageTest extends CoverageColumnTypeTest {
21 |
22 | @Test
23 | void shouldGetCoverage() {
24 | CoverageBuildAction action = createCoverageBuildAction();
25 | Optional coverage = CHANGE_COVERAGE.getCoverage(action, COVERAGE_METRIC);
26 |
27 | assertThat(coverage).isNotEmpty().satisfies(value -> assertThat(value.get()).isEqualTo(COVERAGE_PERCENTAGE));
28 | }
29 |
30 | @Test
31 | void shouldNotReturnNonExistentCoverage() {
32 | CoverageBuildAction action = createCoverageBuildAction();
33 | Optional coverage = CHANGE_COVERAGE.getCoverage(action, CoverageMetric.FILE);
34 |
35 | assertThat(coverage).isEmpty();
36 | }
37 |
38 | @Test
39 | void shouldGetDisplayColors() {
40 | DisplayColors color = CHANGE_COVERAGE.getDisplayColors(COVERAGE_PERCENTAGE);
41 | assertThat(color).isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.VERY_BAD));
42 | }
43 |
44 | @Test
45 | void shouldFormatCoverage() {
46 | String formattedCoverage = CHANGE_COVERAGE.formatCoverage(COVERAGE_PERCENTAGE, LOCALE);
47 | assertThat(formattedCoverage).isEqualTo("50,00%");
48 | }
49 |
50 | @Test
51 | void shouldProvideReportUrl() {
52 | assertThat(CHANGE_COVERAGE.getAnchor()).isEqualTo("#changeCoverage");
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Optional;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
8 | import io.jenkins.plugins.coverage.model.CoverageMetric;
9 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
12 |
13 | import static org.assertj.core.api.Assertions.*;
14 |
15 | /**
16 | * Test class for {@link ProjectCoverage}.
17 | *
18 | * @author Florian Orendi
19 | */
20 | class ProjectCoverageTest extends CoverageColumnTypeTest {
21 |
22 | @Test
23 | void shouldGetCoverage() {
24 | CoverageBuildAction action = createCoverageBuildAction();
25 | Optional coverage = PROJECT_COVERAGE.getCoverage(action, COVERAGE_METRIC);
26 |
27 | assertThat(coverage).isNotEmpty().satisfies(value -> assertThat(value.get()).isEqualTo(COVERAGE_PERCENTAGE));
28 | }
29 |
30 | @Test
31 | void shouldNotReturnNonExistentCoverage() {
32 | CoverageBuildAction action = createCoverageBuildAction();
33 | Optional coverage = PROJECT_COVERAGE.getCoverage(action, CoverageMetric.FILE);
34 |
35 | assertThat(coverage).isEmpty();
36 | }
37 |
38 | @Test
39 | void shouldGetDisplayColors() {
40 | DisplayColors color = PROJECT_COVERAGE.getDisplayColors(COVERAGE_PERCENTAGE);
41 | assertThat(color).isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.VERY_BAD));
42 | }
43 |
44 | @Test
45 | void shouldFormatCoverage() {
46 | String formattedCoverage = PROJECT_COVERAGE.formatCoverage(COVERAGE_PERCENTAGE, LOCALE);
47 | assertThat(formattedCoverage).isEqualTo("50,00%");
48 | }
49 |
50 | @Test
51 | void shouldProvideReportUrl() {
52 | assertThat(PROJECT_COVERAGE.getAnchor()).isEqualTo("#overview");
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/XMLCoverageReportAdapter.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 | import edu.umd.cs.findbugs.annotations.CheckForNull;
4 | import edu.umd.cs.findbugs.annotations.Nullable;
5 | import io.jenkins.plugins.coverage.adapter.util.XMLUtils;
6 | import io.jenkins.plugins.coverage.exception.CoverageException;
7 | import org.apache.commons.lang.StringUtils;
8 | import org.w3c.dom.Document;
9 |
10 | import javax.xml.transform.stream.StreamSource;
11 | import java.io.File;
12 | import java.io.FileNotFoundException;
13 |
14 | public abstract class XMLCoverageReportAdapter extends CoverageReportAdapter {
15 |
16 |
17 | public XMLCoverageReportAdapter(String path) {
18 | super(path);
19 | }
20 |
21 | /**
22 | * @return XSL file that convert report into standard format
23 | */
24 | @CheckForNull
25 | public abstract String getXSL();
26 |
27 | /**
28 | * If return null, report will not be validate.
29 | *
30 | * @return XSD file to validate report
31 | */
32 | @Nullable
33 | public abstract String getXSD();
34 |
35 | /**
36 | * convert source xml file according to xsl file
37 | *
38 | * @param source source xml file
39 | */
40 | @Override
41 | public Document convert(File source) throws CoverageException {
42 | try {
43 | StreamSource xsl = getRealXSL();
44 | return XMLUtils.getInstance().convertToDocumentWithXSL(xsl, source);
45 | } catch (FileNotFoundException e) {
46 | e.printStackTrace();
47 | throw new CoverageException(e);
48 | }
49 | }
50 |
51 | @SuppressWarnings("WeakerAccess")
52 | protected StreamSource getRealXSL() throws FileNotFoundException {
53 | String xsl = getXSL();
54 | if (StringUtils.isEmpty(xsl)) {
55 | throw new FileNotFoundException("Cannot found xsl file, xsl path must be no-empty");
56 | }
57 | return new StreamSource(getXSLResourceClass().getResourceAsStream(xsl));
58 | }
59 |
60 | private Class getXSLResourceClass() {
61 | return this.getClass();
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import hudson.model.View;
6 | import hudson.views.ListViewColumn;
7 |
8 | import io.jenkins.plugins.casc.ConfigurationAsCode;
9 | import io.jenkins.plugins.casc.ConfiguratorException;
10 | import io.jenkins.plugins.coverage.model.visualization.dashboard.CoverageColumn;
11 | import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerTest;
12 |
13 | import static io.jenkins.plugins.coverage.metrics.Assertions.*;
14 |
15 | /**
16 | * Tests the Job DSL Plugin.
17 | *
18 | * @author Ullrich Hafner
19 | */
20 | class JobDslITest extends IntegrationTestWithJenkinsPerTest {
21 | /**
22 | * Creates a freestyle job from a YAML file and verifies that issue recorder finds warnings.
23 | */
24 | @Test
25 | void shouldCreateFreestyleJobUsingJobDslAndVerifyIssueRecorderWithDefaultConfiguration() {
26 | configureJenkins("column-dsl.yaml");
27 |
28 | View view = getJenkins().getInstance().getView("dsl-view");
29 |
30 | assertThat(view).isNotNull();
31 |
32 | assertThat(view.getColumns())
33 | .extracting(ListViewColumn::getColumnCaption)
34 | .contains(new CoverageColumn().getColumnCaption());
35 |
36 | assertThat(view.getColumns()).first()
37 | .isInstanceOfSatisfying(CoverageColumn.class,
38 | c -> {
39 | assertThat(c).hasColumnCaption(Messages.Coverage_Column())
40 | .hasCoverageMetric(CoverageMetric.LINE.getName());
41 | });
42 | }
43 |
44 | /**
45 | * Helper method to get jenkins configuration file.
46 | *
47 | * @param fileName
48 | * file with configuration.
49 | */
50 | private void configureJenkins(final String fileName) {
51 | try {
52 | ConfigurationAsCode.get().configure(getResourceAsFile(fileName).toUri().toString());
53 | }
54 | catch (ConfiguratorException e) {
55 | throw new AssertionError(e);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Optional;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
8 | import io.jenkins.plugins.coverage.model.CoverageMetric;
9 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
12 |
13 | import static org.assertj.core.api.Assertions.*;
14 |
15 | /**
16 | * Test class for {@link ProjectCoverage}.
17 | *
18 | * @author Florian Orendi
19 | */
20 | class ChangeCoverageDeltaTest extends CoverageColumnTypeTest {
21 |
22 | @Test
23 | void shouldGetCoverage() {
24 | CoverageBuildAction action = createCoverageBuildAction();
25 | Optional coverage = CHANGE_COVERAGE_DELTA.getCoverage(action, COVERAGE_METRIC);
26 |
27 | assertThat(coverage).isNotEmpty().satisfies(value -> assertThat(value.get()).isEqualTo(COVERAGE_PERCENTAGE));
28 | }
29 |
30 | @Test
31 | void shouldNotReturnNonExistentCoverage() {
32 | CoverageBuildAction action = createCoverageBuildAction();
33 | Optional coverage = CHANGE_COVERAGE_DELTA.getCoverage(action, CoverageMetric.FILE);
34 |
35 | assertThat(coverage).isEmpty();
36 | }
37 |
38 | @Test
39 | void shouldGetDisplayColors() {
40 | DisplayColors color = CHANGE_COVERAGE_DELTA.getDisplayColors(COVERAGE_PERCENTAGE);
41 | assertThat(color).isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.EXCELLENT));
42 | }
43 |
44 | @Test
45 | void shouldFormatCoverage() {
46 | String formattedCoverage = CHANGE_COVERAGE_DELTA.formatCoverage(COVERAGE_PERCENTAGE, LOCALE);
47 | assertThat(formattedCoverage).isEqualTo("+50,00%");
48 | }
49 |
50 | @Test
51 | void shouldProvideReportUrl() {
52 | assertThat(CHANGE_COVERAGE_DELTA.getAnchor()).isEqualTo("#changeCoverage");
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import nl.jqno.equalsverifier.EqualsVerifier;
6 | import nl.jqno.equalsverifier.Warning;
7 |
8 | import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder;
9 |
10 | import static org.assertj.core.api.Assertions.*;
11 |
12 | /**
13 | * Test class for {@link PackageCoverageNode}.
14 | *
15 | * @author Florian Orendi
16 | */
17 | class PackageCoverageNodeTest {
18 |
19 | private static final String PACKAGE = "test.path";
20 | private static final String PATH = "test/path";
21 |
22 | @Test
23 | void shouldGetPath() {
24 | PackageCoverageNode node = createPackageCoverageNode();
25 | assertThat(node.getPath()).isEqualTo(PATH);
26 | }
27 |
28 | @Test
29 | void shouldCopyTree() {
30 | PackageCoverageNode node = createPackageCoverageNode();
31 | CoverageNode root = node.getParent().copyTree();
32 | assertThat(root.getChildren()).containsExactly(node);
33 | }
34 |
35 | @Test
36 | void shouldObeyEqualsContract() {
37 | EqualsVerifier.forClass(PackageCoverageNode.class)
38 | .withPrefabValues(CoverageNode.class,
39 | new PackageCoverageNode(PACKAGE),
40 | new CoverageNode(CoverageMetric.FILE, "file.txt"))
41 | .suppress(Warning.NONFINAL_FIELDS)
42 | .usingGetClass()
43 | .withIgnoredFields("parent")
44 | .verify();
45 | }
46 |
47 | /**
48 | * Creates an instance of {@link PackageCoverageNode}.
49 | *
50 | * @return the created instance
51 | */
52 | private PackageCoverageNode createPackageCoverageNode() {
53 | CoverageNode parent = new CoverageNode(CoverageMetric.MODULE, "");
54 | CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, CoverageBuilder.NO_COVERAGE);
55 | PackageCoverageNode node = new PackageCoverageNode(PACKAGE);
56 | node.setParent(parent);
57 | parent.add(node);
58 | node.add(leaf);
59 | return node;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Optional;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
8 | import io.jenkins.plugins.coverage.model.CoverageMetric;
9 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
12 |
13 | import static org.assertj.core.api.Assertions.*;
14 |
15 | /**
16 | * Test class for {@link ProjectCoverageDelta}.
17 | *
18 | * @author Florian Orendi
19 | */
20 | class ProjectCoverageDeltaTest extends CoverageColumnTypeTest {
21 |
22 | @Test
23 | void shouldGetCoverage() {
24 | CoverageBuildAction action = createCoverageBuildAction();
25 | Optional coverage = PROJECT_COVERAGE_DELTA.getCoverage(action, COVERAGE_METRIC);
26 |
27 | assertThat(coverage).isNotEmpty().satisfies(value -> assertThat(value.get()).isEqualTo(COVERAGE_PERCENTAGE));
28 | }
29 |
30 | @Test
31 | void shouldNotReturnNonExistentCoverage() {
32 | CoverageBuildAction action = createCoverageBuildAction();
33 | Optional coverage = PROJECT_COVERAGE_DELTA.getCoverage(action, CoverageMetric.FILE);
34 |
35 | assertThat(coverage).isEmpty();
36 | }
37 |
38 | @Test
39 | void shouldGetDisplayColors() {
40 | DisplayColors color = PROJECT_COVERAGE_DELTA.getDisplayColors(COVERAGE_PERCENTAGE);
41 | assertThat(color).isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.EXCELLENT));
42 | }
43 |
44 | @Test
45 | void shouldFormatCoverage() {
46 | String formattedCoverage = PROJECT_COVERAGE_DELTA.formatCoverage(COVERAGE_PERCENTAGE, LOCALE);
47 | assertThat(formattedCoverage).isEqualTo("+50,00%");
48 | }
49 |
50 | @Test
51 | void shouldProvideReportUrl() {
52 | assertThat(PROJECT_COVERAGE_DELTA.getAnchor()).isEqualTo("#overview");
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.dashboard;
2 |
3 | import java.util.Optional;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
8 | import io.jenkins.plugins.coverage.model.CoverageMetric;
9 | import io.jenkins.plugins.coverage.model.CoveragePercentage;
10 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId;
11 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
12 |
13 | import static org.assertj.core.api.Assertions.*;
14 |
15 | /**
16 | * Test class for {@link ProjectCoverage}.
17 | *
18 | * @author Florian Orendi
19 | */
20 | class IndirectCoverageChangesTest extends CoverageColumnTypeTest {
21 |
22 | @Test
23 | void shouldGetCoverage() {
24 | CoverageBuildAction action = createCoverageBuildAction();
25 | Optional coverage = INDIRECT_COVERAGE_CHANGES.getCoverage(action, COVERAGE_METRIC);
26 |
27 | assertThat(coverage).isNotEmpty().satisfies(value -> assertThat(value.get()).isEqualTo(COVERAGE_PERCENTAGE));
28 | }
29 |
30 | @Test
31 | void shouldNotReturnNonExistentCoverage() {
32 | CoverageBuildAction action = createCoverageBuildAction();
33 | Optional coverage = INDIRECT_COVERAGE_CHANGES.getCoverage(action, CoverageMetric.FILE);
34 |
35 | assertThat(coverage).isEmpty();
36 | }
37 |
38 | @Test
39 | void shouldGetDisplayColors() {
40 | DisplayColors color = INDIRECT_COVERAGE_CHANGES.getDisplayColors(COVERAGE_PERCENTAGE);
41 | assertThat(color).isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.VERY_BAD));
42 | }
43 |
44 | @Test
45 | void shouldFormatCoverage() {
46 | String formattedCoverage = INDIRECT_COVERAGE_CHANGES.formatCoverage(COVERAGE_PERCENTAGE, LOCALE);
47 | assertThat(formattedCoverage).isEqualTo("50,00%");
48 | }
49 |
50 | @Test
51 | void shouldProvideReportUrl() {
52 | assertThat(INDIRECT_COVERAGE_CHANGES.getAnchor()).isEqualTo("#indirectCoverage");
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/source/SourceFileResolver.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.source;
2 |
3 | import java.io.IOException;
4 | import java.util.Map;
5 | import java.util.Set;
6 |
7 | import hudson.ExtensionPoint;
8 | import hudson.FilePath;
9 | import hudson.model.Describable;
10 | import hudson.model.Descriptor;
11 | import hudson.model.Run;
12 | import hudson.model.TaskListener;
13 | import jenkins.model.Jenkins;
14 |
15 | import io.jenkins.plugins.coverage.targets.CoveragePaint;
16 |
17 | public abstract class SourceFileResolver implements ExtensionPoint, Describable {
18 | private DefaultSourceFileResolver.SourceFileResolverLevel level;
19 | private Set possiblePaths;
20 |
21 | public SourceFileResolver(final DefaultSourceFileResolver.SourceFileResolverLevel level) {
22 | this.level = level;
23 | }
24 |
25 | public abstract void resolveSourceFiles(Run, ?> run, FilePath workspace, TaskListener listener, Map paints) throws IOException;
26 |
27 | public void setPossiblePaths(final Set possiblePaths) {
28 | this.possiblePaths = possiblePaths;
29 | }
30 |
31 | public Set getPossiblePaths() {
32 | return possiblePaths;
33 | }
34 |
35 | @SuppressWarnings("unchecked")
36 | @Override
37 | public Descriptor getDescriptor() {
38 | return Jenkins.get().getDescriptorOrDie(getClass());
39 | }
40 |
41 | public DefaultSourceFileResolver.SourceFileResolverLevel getLevel() {
42 | return level;
43 | }
44 |
45 | public void setLevel(final DefaultSourceFileResolver.SourceFileResolverLevel level) {
46 | this.level = level;
47 | }
48 |
49 | public enum SourceFileResolverLevel {
50 | NEVER_STORE(Messages.SourceFileResolver_neverSave()),
51 | STORE_LAST_BUILD(Messages.SourceFileResolver_saveLast()),
52 | STORE_ALL_BUILD(Messages.SourceFileResolver_saveAll());
53 |
54 | private final String name;
55 |
56 | SourceFileResolverLevel(final String name) {
57 | this.name = name;
58 | }
59 |
60 | public String getName() {
61 | return name;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/converter/DocumentConverter.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter.converter;
2 |
3 | import io.jenkins.plugins.coverage.exception.CoverageException;
4 | import org.w3c.dom.Document;
5 |
6 | import javax.xml.XMLConstants;
7 | import javax.xml.parsers.DocumentBuilder;
8 | import javax.xml.parsers.DocumentBuilderFactory;
9 | import javax.xml.parsers.ParserConfigurationException;
10 |
11 | public abstract class DocumentConverter {
12 |
13 | /**
14 | * Convert other format report to standard format Document.
15 | *
16 | * @param report other format report
17 | * @return document converted by other report
18 | */
19 | public Document convert(T report) throws CoverageException {
20 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
21 | try {
22 | factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
23 | factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
24 | factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
25 | factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
26 | factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
27 | } catch (ParserConfigurationException e) {
28 | e.printStackTrace();
29 | }
30 |
31 | factory.setNamespaceAware(true);
32 | DocumentBuilder builder;
33 | try {
34 | builder = factory.newDocumentBuilder();
35 | } catch (ParserConfigurationException e) {
36 | throw new CoverageException(e);
37 | }
38 |
39 | Document document = builder.newDocument();
40 |
41 | convert(report, document);
42 |
43 | return document;
44 | }
45 |
46 | /**
47 | * Convert other format report to standard format Document.
48 | * @param report other format report
49 | * @param document document that the report will convert to
50 | * @return document converted by other report
51 | */
52 | protected abstract Document convert(T report, Document document) throws CoverageException;
53 |
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import java.io.Serializable;
4 | import java.util.Objects;
5 |
6 | import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder;
7 |
8 | /**
9 | * A leaf in the coverage hierarchy. A leaf is a non-divisible coverage metric like line or branch coverage.
10 | *
11 | * @author Ullrich Hafner
12 | */
13 | public final class CoverageLeaf implements Serializable {
14 | private static final long serialVersionUID = -1062406664372222691L;
15 |
16 | private final CoverageMetric metric;
17 | private final Coverage coverage;
18 |
19 | /**
20 | * Creates a new leaf with the given coverage for the specified metric.
21 | *
22 | * @param metric
23 | * the coverage metric
24 | * @param coverage
25 | * the coverage of the element
26 | */
27 | public CoverageLeaf(final CoverageMetric metric, final Coverage coverage) {
28 | this.metric = metric;
29 | this.coverage = coverage;
30 | }
31 |
32 | public CoverageMetric getMetric() {
33 | return metric;
34 | }
35 |
36 | /**
37 | * Returns the coverage for the specified metric.
38 | *
39 | * @param searchMetric
40 | * the metric to get the coverage for
41 | *
42 | * @return coverage ratio
43 | */
44 | public Coverage getCoverage(final CoverageMetric searchMetric) {
45 | if (metric.equals(searchMetric)) {
46 | return coverage;
47 | }
48 | return CoverageBuilder.NO_COVERAGE;
49 | }
50 |
51 | @Override
52 | public String toString() {
53 | return String.format("[%s]: %s", metric, coverage);
54 | }
55 |
56 | @Override
57 | public boolean equals(final Object o) {
58 | if (this == o) {
59 | return true;
60 | }
61 | if (o == null || getClass() != o.getClass()) {
62 | return false;
63 | }
64 | CoverageLeaf that = (CoverageLeaf) o;
65 | return Objects.equals(metric, that.metric) && Objects.equals(coverage, that.coverage);
66 | }
67 |
68 | @Override
69 | public int hashCode() {
70 | return Objects.hash(metric, coverage);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import java.util.List;
4 | import java.util.stream.Collectors;
5 |
6 | import edu.hm.hafner.util.FilteredLog;
7 |
8 | import one.util.streamex.StreamEx;
9 |
10 | /**
11 | * Validates the file paths of the processed project files.
12 | *
13 | * @author Florian Orendi
14 | */
15 | class FilePathValidator {
16 |
17 | static final String AMBIGUOUS_FILES_MESSAGE =
18 | "There are ambiguous file paths which might lead to faulty coverage reports:";
19 | static final String REMOVED_MESSAGE =
20 | "-> These files have been removed from the report in order to guarantee flawless coverage visualizations";
21 | static final String PACKAGE_INFO_MESSAGE =
22 | "-> In order to avoid this make sure package names are unique within the whole project";
23 |
24 | /**
25 | * Private default constructor.
26 | */
27 | private FilePathValidator() {
28 | // hides the public constructor
29 | }
30 |
31 | /**
32 | * Verifies that the passed coverage tree only contains files with unique paths. Duplicate paths are logged and
33 | * removed from the coverage tree.
34 | *
35 | * @param root
36 | * The {@link CoverageNode root} of the coverage tree
37 | * @param log
38 | * The log
39 | */
40 | static void verifyPathUniqueness(final CoverageNode root, final FilteredLog log) {
41 | List duplicates = StreamEx.of(root.getAllFileCoverageNodes())
42 | .map(FileCoverageNode::getPath)
43 | .distinct(2)
44 | .collect(Collectors.toList());
45 | if (!duplicates.isEmpty()) {
46 | root.getAllFileCoverageNodes().stream()
47 | .filter(node -> duplicates.contains(node.getPath()))
48 | .forEach(CoverageNode::remove);
49 |
50 | String message = AMBIGUOUS_FILES_MESSAGE + System.lineSeparator()
51 | + String.join("," + System.lineSeparator(), duplicates);
52 | log.logError(message);
53 | log.logError(REMOVED_MESSAGE);
54 | log.logError(PACKAGE_INFO_MESSAGE);
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import edu.hm.hafner.echarts.BuildResult;
4 | import edu.hm.hafner.echarts.ChartModelConfiguration;
5 | import edu.hm.hafner.echarts.LinesChartModel;
6 | import edu.umd.cs.findbugs.annotations.NonNull;
7 |
8 | import hudson.model.Job;
9 |
10 | import io.jenkins.plugins.coverage.Messages;
11 | import io.jenkins.plugins.coverage.model.visualization.charts.CoverageTrendChart;
12 | import io.jenkins.plugins.echarts.AsyncConfigurableTrendJobAction;
13 |
14 | /**
15 | * Project level action for the coverage results. A job action displays a link on the side panel of a job that refers to
16 | * the last build that contains coverage results (i.e. a {@link CoverageBuildAction} with a {@link CoverageNode}
17 | * instance). This action also is responsible to render the historical trend via its associated 'floatingBox.jelly'
18 | * view.
19 | *
20 | * @author Ullrich Hafner
21 | */
22 | public class CoverageJobAction extends AsyncConfigurableTrendJobAction {
23 | CoverageJobAction(final Job, ?> owner) {
24 | super(owner, CoverageBuildAction.class);
25 | }
26 |
27 | @Override
28 | public String getIconFileName() {
29 | return CoverageBuildAction.ICON;
30 | }
31 |
32 | @Override
33 | public String getDisplayName() {
34 | return Messages.CoverageProjectAction_displayName();
35 | }
36 |
37 | @Override @NonNull
38 | public String getUrlName() {
39 | return "coverage";
40 | }
41 |
42 | public Job, ?> getProject() {
43 | return getOwner();
44 | }
45 |
46 | public String getSearchUrl() {
47 | return getUrlName();
48 | }
49 |
50 | @Override
51 | protected LinesChartModel createChartModel(final String configuration) {
52 | return createChart(createBuildHistory(), configuration);
53 | }
54 |
55 | LinesChartModel createChart(final Iterable extends BuildResult> buildHistory,
56 | final String configuration) {
57 | ChartModelConfiguration modelConfiguration = ChartModelConfiguration.fromJson(configuration);
58 | return new CoverageTrendChart().create(buildHistory, modelConfiguration);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/CompatibleObjectInputStream.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage;
2 |
3 | import hudson.remoting.ClassFilter;
4 | import hudson.remoting.ObjectInputStreamEx;
5 |
6 | import java.io.*;
7 |
8 | /**
9 | * {@link io.jenkins.plugins.coverage.targets.CoverageElement} didn't have serialVersionUID. In order to keep
10 | * backwards compatibility, use this class to read serialized Object to avoid that serialVersionUID is different.
11 | */
12 | public class CompatibleObjectInputStream extends ObjectInputStreamEx {
13 |
14 | public CompatibleObjectInputStream(InputStream in) throws IOException {
15 | // TODO Use a classloader in this plugin, or uberClassLoader?
16 | super(in, CoverageProcessor.class.getClassLoader(), ClassFilter.DEFAULT);
17 | }
18 |
19 |
20 | // See https://stackoverflow.com/questions/795470/how-to-deserialize-an-object-persisted-in-a-db-now-when-the-object-has-different?rq=1
21 | @Override
22 | protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
23 | ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
24 | Class localClass = Class.forName(resultClassDescriptor.getName()); // the class in the local JVM that this descriptor represents.
25 | ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
26 | if (localClassDescriptor != null) { // only if class implements serializable
27 | final long localSUID = localClassDescriptor.getSerialVersionUID();
28 | final long streamSUID = resultClassDescriptor.getSerialVersionUID();
29 | if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
30 | String s = String.format("Overriding serialized class %s version mismatch: local serialVersionUID = %d stream serialVersionUID = %d",
31 | resultClassDescriptor.getName(), localSUID, streamSUID);
32 | Exception e = new InvalidClassException(s);
33 | System.out.println("Potentially Fatal Deserialization Operation. " + e);
34 | resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
35 | }
36 | }
37 | return resultClassDescriptor;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/testutil/JobStubs.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.testutil;
2 |
3 | import java.util.Arrays;
4 |
5 | import edu.hm.hafner.util.VisibleForTesting;
6 |
7 | import hudson.model.Build;
8 | import hudson.model.Job;
9 | import hudson.model.Run;
10 |
11 | import io.jenkins.plugins.coverage.model.CoverageBuildAction;
12 |
13 | import static org.mockito.Mockito.*;
14 |
15 | /**
16 | * Provides stubs of Jenkins {@link Job} and {@link Build}.
17 | *
18 | * @author Florian Orendi
19 | */
20 | public final class JobStubs {
21 |
22 | private JobStubs() {
23 | // prevents instantiation
24 | }
25 |
26 | /**
27 | * Creates a stub of {@link Job}.
28 | *
29 | * @return the created stub
30 | */
31 | @VisibleForTesting
32 | public static Job, ?> createJob() {
33 | return mock(Job.class);
34 | }
35 |
36 | /**
37 | * Creates a stub for a {@link Job} that has the specified actions attached.
38 | *
39 | * @param actions
40 | * The actions to attach, might be empty
41 | *
42 | * @return the created stub
43 | */
44 | @VisibleForTesting
45 | public static Job, ?> createJobWithActions(final CoverageBuildAction... actions) {
46 | Job job = createJob();
47 | Run, ?> build = createBuildWithActions(actions);
48 | when(job.getLastCompletedBuild()).thenReturn(build);
49 | return job;
50 | }
51 |
52 | /**
53 | * Creates a stub of {@link Run}.
54 | *
55 | * @return the created stub
56 | */
57 | @VisibleForTesting
58 | public static Run, ?> createBuild() {
59 | return mock(Run.class);
60 | }
61 |
62 | /**
63 | * Creates a stub for a {@link Run} that has the specified actions attached.
64 | *
65 | * @param actions
66 | * the actions to attach, might be empty
67 | *
68 | * @return the created stub
69 | */
70 | @VisibleForTesting
71 | public static Run, ?> createBuildWithActions(final CoverageBuildAction... actions) {
72 | Run, ?> build = createBuild();
73 | when(build.getActions(CoverageBuildAction.class)).thenReturn(Arrays.asList(actions));
74 | if (actions.length > 0) {
75 | when(build.getAction(CoverageBuildAction.class)).thenReturn(actions[0]);
76 | }
77 | return build;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import nl.jqno.equalsverifier.EqualsVerifier;
6 | import nl.jqno.equalsverifier.Warning;
7 |
8 | import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder;
9 |
10 | import static org.assertj.core.api.Assertions.*;
11 |
12 | /**
13 | * Test class for {@link MethodCoverageNode}.
14 | *
15 | * @author Florian Orendi
16 | */
17 | class MethodCoverageNodeTest {
18 |
19 | private static final String NAME = "METHOD";
20 | private static final int LINE = 5;
21 |
22 | @Test
23 | void shouldGetValidLineNumberOfMethod() {
24 | MethodCoverageNode node = createMethodCoverageNode();
25 | assertThat(node.hasValidLineNumber()).isTrue();
26 | assertThat(node.getLineNumber()).isEqualTo(5);
27 | }
28 |
29 | @Test
30 | void shouldRecognizeInvalidLineNumberOfMethod() {
31 | MethodCoverageNode node = new MethodCoverageNode(NAME, 0);
32 | assertThat(node.hasValidLineNumber()).isFalse();
33 | }
34 |
35 | @Test
36 | void shouldCopyTree() {
37 | MethodCoverageNode node = createMethodCoverageNode();
38 | CoverageNode root = node.getParent().copyTree();
39 | assertThat(root.getChildren()).containsExactly(node);
40 | }
41 |
42 | @Test
43 | void shouldObeyEqualsContract() {
44 | EqualsVerifier.forClass(MethodCoverageNode.class)
45 | .withPrefabValues(CoverageNode.class,
46 | new MethodCoverageNode("", LINE),
47 | new CoverageNode(CoverageMetric.LINE, "line"))
48 | .suppress(Warning.NONFINAL_FIELDS)
49 | .usingGetClass()
50 | .withIgnoredFields("parent")
51 | .verify();
52 | }
53 |
54 | /**
55 | * Creates an instance of {@link MethodCoverageNode}.
56 | *
57 | * @return the created instance
58 | */
59 | private MethodCoverageNode createMethodCoverageNode() {
60 | CoverageNode parent = new CoverageNode(CoverageMetric.LINE, "");
61 | CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, CoverageBuilder.NO_COVERAGE);
62 | MethodCoverageNode node = new MethodCoverageNode(NAME, LINE);
63 | node.setParent(parent);
64 | parent.add(node);
65 | node.add(leaf);
66 | return node;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/resources/coverage/configuration.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Provides the configuration for the coverage recorder and step.
6 |
7 |
8 |
9 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/adapter/CoberturaReportAdapter.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.adapter;
2 |
3 | import java.io.File;
4 | import javax.xml.transform.TransformerException;
5 |
6 | import edu.umd.cs.findbugs.annotations.NonNull;
7 | import org.w3c.dom.Document;
8 | import org.w3c.dom.Element;
9 |
10 | import org.kohsuke.stapler.DataBoundConstructor;
11 | import org.jenkinsci.Symbol;
12 | import hudson.Extension;
13 |
14 | import io.jenkins.plugins.coverage.adapter.util.XMLUtils;
15 | import io.jenkins.plugins.coverage.detector.Detectable;
16 |
17 | /**
18 | * Coverage report adapter for Cobertura.
19 | */
20 | public final class CoberturaReportAdapter extends JavaXMLCoverageReportAdapter {
21 |
22 | @DataBoundConstructor
23 | public CoberturaReportAdapter(final String path) {
24 | super(path);
25 | }
26 |
27 | /**
28 | * {@inheritDoc}
29 | */
30 | @Override
31 | public String getXSL() {
32 | return "cobertura-to-standard.xsl";
33 | }
34 |
35 | /**
36 | * {@inheritDoc}
37 | */
38 | @Override
39 | public String getXSD() {
40 | return null;
41 | }
42 |
43 | @Symbol({"cobertura", "coberturaReportAdapter"})
44 | @Extension
45 | public static final class CoberturaReportAdapterDescriptor extends JavaCoverageReportAdapterDescriptor
46 | implements Detectable {
47 |
48 | public CoberturaReportAdapterDescriptor() {
49 | super(CoberturaReportAdapter.class);
50 | }
51 |
52 | /**
53 | * @param file file be detect
54 | * @return true is file is a cobertura report
55 | */
56 | @Override
57 | public boolean detect(final File file) {
58 | if (!file.exists()) {
59 | return false;
60 | }
61 |
62 | Document d;
63 | try {
64 | d = XMLUtils.getInstance().readXMLtoDocument(file);
65 | } catch (TransformerException ignore) {
66 | return false;
67 | }
68 |
69 | Element e = d.getDocumentElement();
70 | if (e == null) {
71 | return false;
72 | }
73 |
74 | return "coverage".equals(e.getLocalName());
75 | }
76 |
77 | @NonNull
78 | @Override
79 | public String getDisplayName() {
80 | return Messages.CoberturaReportAdapter_displayName();
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/CoverageMetricTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import org.junit.jupiter.api.Test;
8 |
9 | import static org.assertj.core.api.Assertions.*;
10 |
11 | /**
12 | * Tests the class {@link CoverageMetric}.
13 | *
14 | * @author Ullrich Hafner
15 | */
16 | class CoverageMetricTest {
17 | @Test
18 | void shouldSortMetrics() {
19 | List all = new ArrayList<>();
20 | all.add(CoverageMetric.MODULE);
21 | all.add(CoverageMetric.PACKAGE);
22 | all.add(CoverageMetric.FILE);
23 | all.add(CoverageMetric.CLASS);
24 | all.add(CoverageMetric.METHOD);
25 | all.add(CoverageMetric.LINE);
26 | all.add(CoverageMetric.BRANCH);
27 | all.add(CoverageMetric.INSTRUCTION);
28 |
29 | Collections.sort(all);
30 | verifyOrder(all);
31 |
32 | Collections.reverse(all);
33 | assertThat(all).containsExactly(
34 | CoverageMetric.BRANCH,
35 | CoverageMetric.INSTRUCTION,
36 | CoverageMetric.LINE,
37 | CoverageMetric.METHOD,
38 | CoverageMetric.CLASS,
39 | CoverageMetric.FILE,
40 | CoverageMetric.PACKAGE,
41 | CoverageMetric.MODULE);
42 |
43 | Collections.sort(all);
44 | verifyOrder(all);
45 | }
46 |
47 | @Test
48 | void shouldGetAvailableMetrics() {
49 | assertThat(CoverageMetric.getAvailableCoverageMetrics()).containsExactlyInAnyOrder(
50 | CoverageMetric.BRANCH,
51 | CoverageMetric.INSTRUCTION,
52 | CoverageMetric.LINE,
53 | CoverageMetric.METHOD,
54 | CoverageMetric.CLASS,
55 | CoverageMetric.FILE,
56 | CoverageMetric.PACKAGE,
57 | CoverageMetric.MODULE
58 | );
59 | }
60 |
61 | private void verifyOrder(final List all) {
62 | assertThat(all).containsExactly(
63 | CoverageMetric.MODULE,
64 | CoverageMetric.PACKAGE,
65 | CoverageMetric.FILE,
66 | CoverageMetric.CLASS,
67 | CoverageMetric.METHOD,
68 | CoverageMetric.LINE,
69 | CoverageMetric.INSTRUCTION,
70 | CoverageMetric.BRANCH
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoverageAction/summary.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ${%Coverage Reports}
7 |
8 |
9 |
10 |
11 |
12 |
36 |
37 |
38 |
39 | ${element.name} :
40 | ${result.getCoverage(element).percentage}%
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ${failMessage}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/CoverageTreeElement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2007-2018 Seiji Sogabe, podarsmarty, Michael Barrientos and Jenkins contributors
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 | package io.jenkins.plugins.coverage.targets;
23 |
24 | import org.kohsuke.stapler.export.Exported;
25 | import org.kohsuke.stapler.export.ExportedBean;
26 |
27 | import java.io.Serializable;
28 |
29 | // Code adopted from Cobertura Plugin https://github.com/jenkinsci/cobertura-plugin/
30 | @ExportedBean(defaultVisibility = 2)
31 | public class CoverageTreeElement implements Serializable {
32 |
33 | /**
34 | * Generated
35 | */
36 | private static final long serialVersionUID = 498666415572813346L;
37 |
38 | private Ratio ratio;
39 |
40 | private CoverageElement element;
41 |
42 | public CoverageTreeElement(CoverageElement element, Ratio ratio) {
43 | this.element = element;
44 | this.ratio = ratio;
45 | }
46 |
47 | @Exported
48 | public String getName() {
49 | return element.getName();
50 | }
51 |
52 | @Exported
53 | public float getRatio() {
54 | return ratio.getPercentageFloat();
55 | }
56 |
57 | @Exported
58 | public float getNumerator() {
59 | return ratio.numerator;
60 | }
61 |
62 | @Exported
63 | public float getDenominator() {
64 | return ratio.denominator;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/CoverageProcessorTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage;
2 |
3 | import hudson.FilePath;
4 | import hudson.model.Run;
5 | import io.jenkins.plugins.coverage.exception.CoverageException;
6 | import io.jenkins.plugins.coverage.targets.CoverageResult;
7 | import org.junit.Assert;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.mockito.Mockito;
11 |
12 | import static org.mockito.ArgumentMatchers.any;
13 | import static org.mockito.Mockito.*;
14 |
15 | /**
16 | * Acceptance tests for CoverageProcessor. Verifies if set options in CoverageProcessor are used and lead to accepted
17 | * results.
18 | */
19 | public class CoverageProcessorTest {
20 |
21 | CoverageProcessor coverageProcessor;
22 | CoverageResult coverageResult;
23 | CoverageAction coverageAction;
24 |
25 | @Before()
26 | public void testInit(){
27 | coverageProcessor = new CoverageProcessor(mock(Run.class),mock(FilePath.class),null);
28 | coverageResult =mock(CoverageResult.class);
29 | coverageAction =mock(CoverageAction.class);
30 | }
31 |
32 | /**
33 | * Test roundFloat method in CoverageProcessor
34 | */
35 | @Test
36 | public void testFloatRounding() {
37 | float number=-0.001f;
38 | float scaledNumber= CoverageProcessor.roundFloat(number,2);
39 | Assert.assertEquals(scaledNumber,0.00,0);
40 | }
41 |
42 | @Test()
43 | public void testChangeRequestDecreasedCoverageNotFailed_NoChange() throws CoverageException {
44 | Mockito.when(coverageResult.getCoverageDelta(any())).thenReturn(-0.001f);
45 | //No Exception should be thrown
46 | coverageProcessor.failBuildIfChangeRequestDecreasedCoverage(coverageResult,coverageAction);
47 | verify(coverageAction, times(0)).setFailMessage(any());
48 | }
49 |
50 | @Test(expected = CoverageException.class)
51 | public void testChangeRequestDecreasedCoverageFailed_DecCoverage() throws CoverageException {
52 | Mockito.when(coverageResult.getCoverageDelta(any())).thenReturn(-1.11f);
53 | coverageProcessor.failBuildIfChangeRequestDecreasedCoverage(coverageResult,coverageAction);
54 | verify(coverageAction, times(1)).setFailMessage(any());
55 | }
56 |
57 | @Test()
58 | public void testChangeRequestDecreasedCoverageNotFailed_IncCoverage() throws CoverageException {
59 | Mockito.when(coverageResult.getCoverageDelta(any())).thenReturn(0.1f);
60 | coverageProcessor.failBuildIfChangeRequestDecreasedCoverage(coverageResult,coverageAction);
61 | verify(coverageAction, times(0)).setFailMessage(any());
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/PluginArchitectureTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import com.tngtech.archunit.junit.AnalyzeClasses;
4 | import com.tngtech.archunit.junit.ArchTest;
5 | import com.tngtech.archunit.lang.ArchRule;
6 |
7 | import edu.hm.hafner.util.ArchitectureRules;
8 |
9 | import io.jenkins.plugins.util.PluginArchitectureRules;
10 |
11 | /**
12 | * Defines several architecture rules for the static analysis model and parsers.
13 | *
14 | * @author Ullrich Hafner
15 | */
16 | @SuppressWarnings("hideutilityclassconstructor")
17 | @AnalyzeClasses(packages = "io.jenkins.plugins.coverage.model")
18 | class PluginArchitectureTest {
19 | @ArchTest
20 | static final ArchRule NO_EXCEPTIONS_WITH_NO_ARG_CONSTRUCTOR = ArchitectureRules.NO_EXCEPTIONS_WITH_NO_ARG_CONSTRUCTOR;
21 |
22 | @ArchTest
23 | static final ArchRule NO_PUBLIC_TEST_CLASSES = ArchitectureRules.NO_PUBLIC_TEST_CLASSES;
24 |
25 | @ArchTest
26 | static final ArchRule NO_PUBLIC_TEST_METHODS = ArchitectureRules.ONLY_PACKAGE_PRIVATE_TEST_METHODS;
27 |
28 | @ArchTest
29 | static final ArchRule NO_TEST_API_CALLED = ArchitectureRules.NO_TEST_API_CALLED;
30 |
31 | @ArchTest
32 | static final ArchRule NO_FORBIDDEN_ANNOTATION_USED = ArchitectureRules.NO_FORBIDDEN_ANNOTATION_USED;
33 |
34 | @ArchTest
35 | static final ArchRule NO_FORBIDDEN_CLASSES_CALLED = ArchitectureRules.NO_FORBIDDEN_CLASSES_CALLED;
36 |
37 | @ArchTest
38 | static final ArchRule ONLY_PACKAGE_PRIVATE_ARCHITECTURE_TESTS = ArchitectureRules.ONLY_PACKAGE_PRIVATE_ARCHITECTURE_TESTS;
39 |
40 | @ArchTest
41 | static final ArchRule NO_JENKINS_INSTANCE_CALL = PluginArchitectureRules.NO_JENKINS_INSTANCE_CALL;
42 |
43 | @ArchTest
44 | static final ArchRule NO_FORBIDDEN_PACKAGE_ACCESSED = PluginArchitectureRules.NO_FORBIDDEN_PACKAGE_ACCESSED;
45 |
46 | @ArchTest
47 | static final ArchRule AJAX_PROXY_METHOD_MUST_BE_IN_PUBLIC_CLASS = PluginArchitectureRules.AJAX_PROXY_METHOD_MUST_BE_IN_PUBLIC_CLASS;
48 |
49 | @ArchTest
50 | static final ArchRule DATA_BOUND_CONSTRUCTOR_MUST_BE_IN_PUBLIC_CLASS = PluginArchitectureRules.DATA_BOUND_CONSTRUCTOR_MUST_BE_IN_PUBLIC_CLASS;
51 |
52 | @ArchTest
53 | static final ArchRule DATA_BOUND_SETTER_MUST_BE_IN_PUBLIC_CLASS = PluginArchitectureRules.DATA_BOUND_SETTER_MUST_BE_IN_PUBLIC_CLASS;
54 |
55 | @ArchTest
56 | static final ArchRule USE_POST_FOR_VALIDATION_END_POINTS = PluginArchitectureRules.USE_POST_FOR_VALIDATION_END_POINTS;
57 |
58 | @ArchTest
59 | static final ArchRule USE_POST_FOR_LIST_MODELS_RULE = PluginArchitectureRules.USE_POST_FOR_LIST_AND_COMBOBOX_FILL;
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/CoverageProjectAction/floatingBox.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | ${%Code Coverage}
12 |
13 |
14 |
15 |
35 |
36 |
37 |
38 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevelTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import java.awt.*;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
8 |
9 | import static org.assertj.core.api.Assertions.*;
10 |
11 | /**
12 | * Test class for {@link CoverageChangeLevel}.
13 | *
14 | * @author Florian Orendi
15 | */
16 | class CoverageChangeLevelTest {
17 |
18 | private static final ColorProvider COLOR_PROVIDER = ColorProviderFactory.createDefaultColorProvider();
19 |
20 | @Test
21 | void shouldHaveWorkingGetters() {
22 | CoverageChangeLevel coverageChangeLevel = CoverageChangeLevel.INCREASE_2;
23 | assertThat(coverageChangeLevel.getChange()).isEqualTo(2.0);
24 | assertThat(coverageChangeLevel.getColorizationId()).isEqualTo(ColorId.VERY_GOOD);
25 | }
26 |
27 | @Test
28 | void shouldGetDisplayColorsOfCoveragePercentage() {
29 | Color blendedLineColor = COLOR_PROVIDER.getDisplayColorsOf(ColorId.BLACK).getFillColor();
30 | Color blendedColorIncreased = ColorProvider.blendColors(
31 | COLOR_PROVIDER.getDisplayColorsOf(CoverageChangeLevel.INCREASE_2.getColorizationId()).getFillColor(),
32 | COLOR_PROVIDER.getDisplayColorsOf(CoverageChangeLevel.EQUALS.getColorizationId()).getFillColor());
33 | Color blendedColorDecreased = ColorProvider.blendColors(
34 | COLOR_PROVIDER.getDisplayColorsOf(CoverageChangeLevel.DECREASE_2.getColorizationId()).getFillColor(),
35 | COLOR_PROVIDER.getDisplayColorsOf(CoverageChangeLevel.EQUALS.getColorizationId()).getFillColor());
36 |
37 | assertThat(CoverageChangeLevel.getDisplayColorsOfCoverageChange(1.0, COLOR_PROVIDER))
38 | .isEqualTo(new DisplayColors(blendedLineColor, blendedColorIncreased));
39 | assertThat(CoverageChangeLevel.getDisplayColorsOfCoverageChange(-1.0, COLOR_PROVIDER))
40 | .isEqualTo(new DisplayColors(blendedLineColor, blendedColorDecreased));
41 | assertThat(CoverageChangeLevel.getDisplayColorsOfCoverageChange(7.0, COLOR_PROVIDER))
42 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.EXCELLENT));
43 | assertThat(CoverageChangeLevel.getDisplayColorsOfCoverageChange(-2.0, COLOR_PROVIDER))
44 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.INADEQUATE));
45 | assertThat(CoverageChangeLevel.getDisplayColorsOfCoverageChange(-110.0, COLOR_PROVIDER))
46 | .isEqualTo(COLOR_PROVIDER.getDisplayColorsOf(ColorId.WHITE));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/resources/io/jenkins/plugins/coverage/threshold/Threshold/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | ${i.name}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
42 |
43 |
44 |
46 |
47 |
48 |
50 |
51 |
52 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/main/resources/coverage/deprecated-coverage-table.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Provides a table to render the file coverage nodes without the source code.
6 |
7 | The ID of the table.
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
${%Please select a file in the table to open the source code}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
${%No source code available for this file}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevel.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model.visualization.colorization;
2 |
3 | import edu.umd.cs.findbugs.annotations.NonNull;
4 |
5 | import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors;
6 |
7 | /**
8 | * Provides the colorization for different coverage change levels.
9 | *
10 | * @author Florian Orendi
11 | */
12 | public enum CoverageChangeLevel {
13 |
14 | INCREASE_5(5.0, ColorId.EXCELLENT),
15 | INCREASE_2(2.0, ColorId.VERY_GOOD),
16 | EQUALS(0.0, ColorId.AVERAGE),
17 | DECREASE_2(-2.0, ColorId.INADEQUATE),
18 | DECREASE_5(-5.0, ColorId.BAD),
19 | DECREASE_10(-10.0, ColorId.VERY_BAD),
20 | DECREASE_20(-20.0, ColorId.INSUFFICIENT),
21 | NA(-100.0, ColorId.WHITE);
22 |
23 | private final double change;
24 | private final ColorId colorizationId;
25 |
26 | CoverageChangeLevel(final double change, final ColorId colorizationId) {
27 | this.change = change;
28 | this.colorizationId = colorizationId;
29 | }
30 |
31 | /**
32 | * Gets the {@link DisplayColors display colors} for representing the passed coverage change. If the change is
33 | * placed between two levels, the fill colors are blended.
34 | *
35 | * @param coverageDifference
36 | * The coverage change
37 | * @param colorProvider
38 | * The {@link ColorProvider color provider} to be used
39 | *
40 | * @return the display colors
41 | */
42 | public static DisplayColors getDisplayColorsOfCoverageChange(final double coverageDifference,
43 | @NonNull final ColorProvider colorProvider) {
44 | for (int i = 0; i < values().length - 1; i++) {
45 | CoverageChangeLevel level = values()[i];
46 | if (coverageDifference >= level.change) {
47 | if (i == 0) {
48 | return colorProvider.getDisplayColorsOf(level.colorizationId);
49 | }
50 | double distanceLevel = coverageDifference - level.change;
51 | if (distanceLevel == 0) {
52 | return colorProvider.getDisplayColorsOf(level.colorizationId);
53 | }
54 | CoverageChangeLevel upperLevel = values()[i - 1];
55 | double distanceUpper = upperLevel.change - coverageDifference;
56 | return colorProvider.getBlendedDisplayColors(
57 | distanceLevel, distanceUpper,
58 | upperLevel.colorizationId,
59 | level.colorizationId);
60 | }
61 | }
62 | return colorProvider.getDisplayColorsOf(NA.colorizationId);
63 | }
64 |
65 | public double getChange() {
66 | return change;
67 | }
68 |
69 | public ColorId getColorizationId() {
70 | return colorizationId;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/CoverageElementRegister.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.targets;
2 |
3 |
4 | import java.util.*;
5 | import java.util.stream.Collectors;
6 |
7 | public class CoverageElementRegister {
8 |
9 | private static Map> typedRegisteredElements = new HashMap<>();
10 |
11 | static {
12 | addCoverageElement(CoverageElement.AGGREGATED_REPORT);
13 | addCoverageElement(CoverageElement.REPORT);
14 | addCoverageElement(CoverageElement.LINE);
15 | addCoverageElement(CoverageElement.CONDITIONAL);
16 | }
17 |
18 | public static boolean addCoverageElement(CoverageElement element) {
19 | return addCoverageElement(CoverageElement.COVERAGE_ELEMENT_TYPE_NONE, element);
20 | }
21 |
22 | public static boolean addCoverageElement(String type, CoverageElement element) {
23 | typedRegisteredElements.putIfAbsent(type, new LinkedList<>());
24 | return typedRegisteredElements.get(type).add(element);
25 | }
26 |
27 | public static boolean addCoverageElements(List elements) {
28 | return addCoverageElements(CoverageElement.COVERAGE_ELEMENT_TYPE_NONE, elements);
29 | }
30 |
31 | public static boolean addCoverageElements(String type, List elements) {
32 | typedRegisteredElements.putIfAbsent(type, new LinkedList<>());
33 | return typedRegisteredElements.get(type).addAll(elements);
34 | }
35 |
36 | public static CoverageElement get(String type, String name) {
37 | return typedRegisteredElements.get(type).stream().filter(c -> c.is(name)).findAny().orElse(null);
38 | }
39 |
40 | public static CoverageElement getDespiteType(String name) {
41 | return typedRegisteredElements.values().stream()
42 | .flatMap(Collection::stream)
43 | .filter(c -> c.is(name))
44 | .findAny().orElse(null);
45 | }
46 |
47 | public static CoverageElement[] all() {
48 | return typedRegisteredElements.values().stream()
49 | .flatMap(Collection::stream)
50 | .distinct()
51 | .sorted()
52 | .collect(Collectors.toList())
53 | .toArray(new CoverageElement[]{});
54 | }
55 |
56 | public static CoverageElement[] listByType(String type) {
57 | return typedRegisteredElements.get(type).toArray(new CoverageElement[]{});
58 | }
59 |
60 |
61 | public static CoverageElement[] listCommonsAndSpecificType(String type) {
62 | CoverageElement[] elements = typedRegisteredElements.entrySet().stream()
63 | .filter(e -> e.getKey().equals(CoverageElement.COVERAGE_ELEMENT_TYPE_NONE) || e.getKey().equals(type))
64 | .flatMap(e -> e.getValue().stream())
65 | .distinct()
66 | .collect(Collectors.toList())
67 | .toArray(new CoverageElement[]{});
68 |
69 | Arrays.sort(elements);
70 | return elements;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/CoverageProjectAction.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | package io.jenkins.plugins.coverage;
4 |
5 | import hudson.model.Actionable;
6 | import hudson.model.Job;
7 | import hudson.model.ProminentProjectAction;
8 | import hudson.model.Result;
9 | import hudson.model.Run;
10 | import org.kohsuke.stapler.StaplerRequest;
11 | import org.kohsuke.stapler.StaplerResponse;
12 |
13 | import java.io.IOException;
14 |
15 | /**
16 | * Project level action.
17 | *
18 | * @author Stephen Connolly
19 | */
20 | public class CoverageProjectAction extends Actionable implements ProminentProjectAction {
21 |
22 | private transient Run, ?> run;
23 |
24 | public CoverageProjectAction(Run, ?> run) {
25 | this.run = run;
26 | }
27 |
28 | /**
29 | * {@inheritDoc}
30 | */
31 | public String getIconFileName() {
32 | return "graph.gif";
33 | }
34 |
35 | /**
36 | * {@inheritDoc}
37 | */
38 | public String getDisplayName() {
39 | return Messages.CoverageProjectAction_displayName();
40 | }
41 |
42 | /**
43 | * {@inheritDoc}
44 | */
45 | public String getUrlName() {
46 | return "coverage";
47 | }
48 |
49 | /**
50 | * Getter for property 'lastResult'.
51 | *
52 | * @return Value for property 'lastResult'.
53 | */
54 | public CoverageAction getLastResult() {
55 | for (Run, ?> b = run.getParent().getLastSuccessfulBuild(); b != null; b = BuildUtils.getPreviousNotFailedCompletedBuild(b)) {
56 | if (b.getResult() != Result.SUCCESS && b.getResult() != Result.UNSTABLE)
57 | continue;
58 | CoverageAction r = b.getAction(CoverageAction.class);
59 | if (r != null)
60 | return r;
61 | }
62 | return null;
63 | }
64 |
65 | /**
66 | * Getter for property 'lastResult'.
67 | *
68 | * @return Value for property 'lastResult'.
69 | */
70 | public Integer getLastResultBuild() {
71 | for (Run, ?> b = run.getParent().getLastSuccessfulBuild(); b != null; b = BuildUtils.getPreviousNotFailedCompletedBuild(b)) {
72 | if (b.getResult() != Result.SUCCESS && b.getResult() != Result.UNSTABLE)
73 | continue;
74 | CoverageAction r = b.getAction(CoverageAction.class);
75 | if (r != null)
76 | return b.getNumber();
77 | }
78 | return null;
79 | }
80 |
81 | public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException {
82 | Integer buildNumber = getLastResultBuild();
83 | if (buildNumber == null) {
84 | rsp.sendRedirect2("nodata");
85 | } else {
86 | rsp.sendRedirect2("../" + buildNumber + "/coverage");
87 | }
88 | }
89 |
90 | public Job, ?> getProject() {
91 | return run.getParent();
92 | }
93 |
94 | /**
95 | * {@inheritDoc}
96 | */
97 | public String getSearchUrl() {
98 | return getUrlName();
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/targets/CoverageElement.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.targets;
2 |
3 | import edu.umd.cs.findbugs.annotations.NonNull;
4 |
5 | import java.io.Serializable;
6 | import java.util.Objects;
7 |
8 | public class CoverageElement implements Comparable, Serializable {
9 | private static final long serialVersionUID = 6722992955158201174L;
10 |
11 | public final static CoverageElement AGGREGATED_REPORT = new CoverageElement("Aggregated Report", Integer.MIN_VALUE);
12 | public final static CoverageElement REPORT = new CoverageElement("Report", Integer.MIN_VALUE + 1);
13 |
14 | public final static CoverageElement FILE = new CoverageElement("File", 2);
15 |
16 | public final static CoverageElement LINE = new CoverageElement("Line", Integer.MAX_VALUE - 1, true);
17 | public final static CoverageElement CONDITIONAL = new CoverageElement("Conditional", Integer.MAX_VALUE, true);
18 |
19 | public final static String COVERAGE_ELEMENT_TYPE_NONE = "None";
20 | public final static String COVERAGE_ELEMENT_TYPE_JAVA = "Java";
21 | public final static String COVERAGE_ELEMENT_TYPE_JAVASCRIPT = "JavaScript";
22 |
23 | private final String name;
24 | private final int order;
25 | private final boolean isBasicBlock;
26 |
27 | public CoverageElement(final String name, final int order) {
28 | this(name, order, false);
29 | }
30 |
31 | public CoverageElement(final String name, final int order, final boolean isBasicBlock) {
32 | this.name = name;
33 | this.order = order;
34 | this.isBasicBlock = isBasicBlock;
35 | }
36 |
37 | public String getName() {
38 | return name;
39 | }
40 |
41 | public boolean isBasicBlock() {
42 | return isBasicBlock;
43 | }
44 |
45 | public boolean is(final String name) {
46 | return this.name.equals(name);
47 | }
48 |
49 | @Override
50 | public boolean equals(final Object o) {
51 | if (this == o) return true;
52 | if (o == null || getClass() != o.getClass()) return false;
53 | CoverageElement that = (CoverageElement) o;
54 | return Objects.equals(name, that.name);
55 | }
56 |
57 | @Override
58 | public String toString() {
59 | return name;
60 | }
61 |
62 | @Override
63 | public int hashCode() {
64 | return Objects.hash(name);
65 | }
66 |
67 | @Override
68 | public int compareTo(@NonNull final CoverageElement coverageElement) {
69 | if (this.order == coverageElement.order) {
70 | return 0;
71 | }
72 |
73 | return this.order < coverageElement.order ? -1 : 1;
74 | }
75 |
76 | public static CoverageElement get(final String name) {
77 | // Type element are equal when their names are equal,
78 | // so we can get CoverageElement just by name to keep the code simpler
79 | return CoverageElementRegister.getDespiteType(name);
80 | }
81 |
82 | public static CoverageElement get(final String type, final String name) {
83 | return CoverageElementRegister.get(type, name);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/io/jenkins/plugins/coverage/model/SafeFraction.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage.model;
2 |
3 | import org.apache.commons.lang3.math.Fraction;
4 |
5 | /**
6 | * A small wrapper for {@link Fraction} instances that avoids an arithmetic overflow by using double based operations in
7 | * case of an exception.
8 | *
9 | * @author Ullrich Hafner
10 | */
11 | public class SafeFraction {
12 | private final Fraction fraction;
13 |
14 | /**
15 | * Creates a new fraction instance that wraps the specified fraction with safe operations.
16 | *
17 | * @param fraction
18 | * the fraction to wrap
19 | */
20 | public SafeFraction(final Fraction fraction) {
21 | this.fraction = fraction;
22 | }
23 |
24 | /**
25 | * Multiplies the value of this fraction by another, returning the result in reduced form. Since there might be an
26 | * arithmetic exception due to an overflow, this method will handle this situation by calculating the multiplication
27 | * based on the double values of the fractions.
28 | *
29 | * @param multiplicator
30 | * the fraction to multiply by
31 | *
32 | * @return a {@code Fraction} instance with the resulting values
33 | */
34 | public Fraction multiplyBy(final Fraction multiplicator) {
35 | try {
36 | return fraction.multiplyBy(multiplicator);
37 | }
38 | catch (ArithmeticException exception) {
39 | return Fraction.getFraction(fraction.doubleValue() * multiplicator.doubleValue());
40 | }
41 | }
42 |
43 | /**
44 | * Subtracts the value of another fraction from the value of this one, returning the result in reduced form. Since
45 | * there might be an arithmetic exception due to an overflow, this method will handle this situation by calculating
46 | * the subtraction based on the double values of the fractions.
47 | *
48 | * @param subtrahend
49 | * the fraction to subtract
50 | *
51 | * @return a {@code Fraction} instance with the resulting values
52 | */
53 | public Fraction subtract(final Fraction subtrahend) {
54 | try {
55 | return fraction.subtract(subtrahend);
56 | }
57 | catch (ArithmeticException exception) {
58 | return Fraction.getFraction(fraction.doubleValue() - subtrahend.doubleValue());
59 | }
60 | }
61 |
62 | /**
63 | * Adds the value of another fraction to the value of this one, returning the result in reduced form. Since
64 | * there might be an arithmetic exception due to an overflow, this method will handle this situation by calculating
65 | * the addition based on the double values of the fractions.
66 | *
67 | * @param summand
68 | * the fraction to add
69 | *
70 | * @return a {@code Fraction} instance with the resulting values
71 | */
72 | public Fraction add(final Fraction summand) {
73 | try {
74 | return fraction.add(summand);
75 | }
76 | catch (ArithmeticException exception) {
77 | return Fraction.getFraction(fraction.doubleValue() + summand.doubleValue());
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/java/io/jenkins/plugins/coverage/CoverageCornerCaseTest.java:
--------------------------------------------------------------------------------
1 | package io.jenkins.plugins.coverage;
2 |
3 | import hudson.FilePath;
4 | import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter;
5 | import org.apache.commons.io.FileUtils;
6 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
7 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
8 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
9 | import org.junit.Assert;
10 | import org.junit.Rule;
11 | import org.junit.Test;
12 | import org.jvnet.hudson.test.JenkinsRule;
13 |
14 | import java.nio.file.Files;
15 | import java.nio.file.Path;
16 | import java.util.Objects;
17 |
18 | import io.jenkins.plugins.coverage.adapter.util.XMLUtils;
19 | import java.io.File;
20 | import org.w3c.dom.Document;
21 |
22 | public class CoverageCornerCaseTest {
23 |
24 | @Rule
25 | public JenkinsRule j = new JenkinsRule();
26 |
27 | @Test
28 | public void testIfNoReportFound() throws Exception {
29 | CoverageScriptedPipelineScriptBuilder builder = CoverageScriptedPipelineScriptBuilder.builder();
30 |
31 | WorkflowJob project = j.createProject(WorkflowJob.class, "coverage-corner-test");
32 | project.setDefinition(new CpsFlowDefinition(builder.build(), true));
33 |
34 | WorkflowRun r = Objects.requireNonNull(project.scheduleBuild2(0)).waitForStart();
35 | Assert.assertNotNull(r);
36 |
37 | j.assertBuildStatusSuccess(j.waitForCompletion(r));
38 | j.assertLogContains("No reports were found", r);
39 | }
40 |
41 | @Test
42 | public void testIfFoundEmptyReport() throws Exception {
43 | CoverageScriptedPipelineScriptBuilder builder = CoverageScriptedPipelineScriptBuilder.builder()
44 | .addAdapter(new JacocoReportAdapter("jacoco.xml"));
45 |
46 | WorkflowJob project = j.createProject(WorkflowJob.class, "coverage-corner-test");
47 | FilePath workspace = j.jenkins.getWorkspaceFor(project);
48 |
49 | Path emptyFile = Files.createTempFile("test", "found-empty-report");
50 |
51 | Objects.requireNonNull(workspace)
52 | .child("jacoco.xml")
53 | .copyFrom(emptyFile.toUri().toURL());
54 | FileUtils.deleteQuietly(emptyFile.toFile());
55 |
56 | workspace.child("cobertura-coverage.xml");
57 | FileUtils.deleteQuietly(emptyFile.toFile());
58 |
59 | project.setDefinition(new CpsFlowDefinition(builder.build(), true));
60 |
61 | WorkflowRun r = Objects.requireNonNull(project.scheduleBuild2(0)).waitForStart();
62 |
63 |
64 | Assert.assertNotNull(r);
65 |
66 | j.assertBuildStatusSuccess(j.waitForCompletion(r));
67 | j.assertLogContains("No reports were found", r);
68 | }
69 |
70 |
71 | @Test
72 | public void testPreventXXE() throws Exception {
73 | /*
74 | Test for SECURITY-1699: if external entities are executed an exception will be thrown
75 | as an invalid external entity (unknown protocol foobar) is defined in the supplied XML
76 | test file
77 | */
78 | Document d;
79 | File file = new File(getClass().getResource("sec1699.xml").toURI());
80 | d = XMLUtils.getInstance().readXMLtoDocument(file);
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------