Skip to content

Commit

Permalink
Fix quality gates for all baselines.
Browse files Browse the repository at this point in the history
  • Loading branch information
uhafner committed Nov 16, 2023
1 parent 56e8f03 commit aac84dd
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 130 deletions.
2 changes: 1 addition & 1 deletion plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<gitHubRepo>jenkinsci/coverage-plugin</gitHubRepo>

<!-- Library Dependencies Versions -->
<coverage-model.version>0.30.0</coverage-model.version>
<coverage-model.version>0.31.0</coverage-model.version>
<jsoup.version>1.16.2</jsoup.version>

<!-- Jenkins Plug-in Dependencies Versions -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.jenkins.plugins.coverage.metrics.steps;

import java.util.ArrayList;
import java.util.List;
import java.util.NavigableMap;
import java.util.Optional;
Expand Down Expand Up @@ -32,13 +31,16 @@

/**
* Transforms the old model to the new model and invokes all steps that work on the new model. Currently, only the
* source code painting and copying has been moved to this new reporter class.
* source code painting and copying have been moved to this new reporter class.
*
* @author Ullrich Hafner
*/
@SuppressWarnings("checkstyle:ClassDataAbstractionCoupling")
@SuppressWarnings({"checkstyle:ClassDataAbstractionCoupling", "PMD.LooseCoupling"})
public class CoverageReporter {
@SuppressWarnings("checkstyle:ParameterNumber")
private static final NavigableMap<Metric, Fraction> EMPTY_DELTA = new TreeMap<>();
private static final List<Value> EMPTY_VALUES = List.of();

@SuppressWarnings({"checkstyle:ParameterNumber", "checkstyle:JavaNCSS"})
CoverageBuildAction publishAction(final String id, final String optionalName, final String icon, final Node rootNode,
final Run<?, ?> build, final FilePath workspace, final TaskListener listener,
final List<CoverageQualityGate> qualityGates, final String scm, final String sourceCodeEncoding,
Expand All @@ -63,29 +65,34 @@ CoverageBuildAction publishAction(final String id, final String optionalName, fi

Node modifiedLinesCoverageRoot = rootNode.filterByModifiedLines();

NavigableMap<Metric, Fraction> modifiedLinesCoverageDelta;
List<Value> aggregatedModifiedFilesCoverage;
NavigableMap<Metric, Fraction> modifiedFilesCoverageDelta;
NavigableMap<Metric, Fraction> modifiedLinesDelta;
List<Value> modifiedFilesValues;
NavigableMap<Metric, Fraction> modifiedFilesDelta;
if (hasModifiedLinesCoverage(modifiedLinesCoverageRoot)) {
Node modifiedFilesCoverageRoot = rootNode.filterByModifiedFiles();
aggregatedModifiedFilesCoverage = modifiedFilesCoverageRoot.aggregateValues();
modifiedFilesCoverageDelta = modifiedFilesCoverageRoot.computeDelta(rootNode);
modifiedLinesCoverageDelta = modifiedLinesCoverageRoot.computeDelta(modifiedFilesCoverageRoot);
modifiedFilesValues = modifiedFilesCoverageRoot.aggregateValues();
modifiedFilesDelta = modifiedFilesCoverageRoot.computeDelta(
referenceRoot.filterByFileNames(modifiedFilesCoverageRoot.getFiles()));

modifiedLinesDelta = modifiedLinesCoverageRoot.computeDelta(modifiedFilesCoverageRoot);
}
else {
modifiedLinesCoverageDelta = new TreeMap<>();
aggregatedModifiedFilesCoverage = new ArrayList<>();
modifiedFilesCoverageDelta = new TreeMap<>();
modifiedLinesDelta = EMPTY_DELTA;
modifiedFilesValues = EMPTY_VALUES;
modifiedFilesDelta = EMPTY_DELTA;

if (rootNode.hasModifiedLines()) {
log.logInfo("No detected code changes affect the code coverage");
}
}

NavigableMap<Metric, Fraction> coverageDelta = rootNode.computeDelta(referenceRoot);
var overallValues = rootNode.aggregateValues();
NavigableMap<Metric, Fraction> overallDelta = rootNode.computeDelta(referenceRoot);
var modifiedLinesValues = modifiedLinesCoverageRoot.aggregateValues();

QualityGateResult qualityGateResult = evaluateQualityGates(rootNode, log,
modifiedLinesCoverageRoot.aggregateValues(), modifiedLinesCoverageDelta, coverageDelta,
resultHandler, qualityGates);
var statistics = new CoverageStatistics(overallValues, overallDelta,
modifiedLinesValues, modifiedLinesDelta, modifiedFilesValues, modifiedFilesDelta);
QualityGateResult qualityGateResult = evaluateQualityGates(qualityGates, statistics, resultHandler, log);

if (sourceCodeRetention == SourceCodeRetention.MODIFIED) {
filesToStore = modifiedLinesCoverageRoot.getAllFileNodes();
Expand All @@ -96,14 +103,16 @@ CoverageBuildAction publishAction(final String id, final String optionalName, fi
}

action = new CoverageBuildAction(build, id, optionalName, icon, rootNode, qualityGateResult, log,
referenceAction.getOwner().getExternalizableId(), coverageDelta,
modifiedLinesCoverageRoot.aggregateValues(), modifiedLinesCoverageDelta,
aggregatedModifiedFilesCoverage, modifiedFilesCoverageDelta,
referenceAction.getOwner().getExternalizableId(), overallDelta,
modifiedLinesValues, modifiedLinesDelta,
modifiedFilesValues, modifiedFilesDelta,
rootNode.filterByIndirectChanges().aggregateValues());
}
else {
QualityGateResult qualityGateStatus = evaluateQualityGates(rootNode, log,
List.of(), new TreeMap<>(), new TreeMap<>(), resultHandler, qualityGates);
var statistics = new CoverageStatistics(rootNode.aggregateValues(),
EMPTY_DELTA, EMPTY_VALUES, EMPTY_DELTA, EMPTY_VALUES, EMPTY_DELTA);
QualityGateResult qualityGateStatus = evaluateQualityGates(qualityGates,
statistics, resultHandler, log);

filesToStore = rootNode.getAllFileNodes();

Expand Down Expand Up @@ -152,16 +161,11 @@ private void createDeltaReports(final Node rootNode, final FilteredLog log, fina
}
}

private QualityGateResult evaluateQualityGates(final Node rootNode, final FilteredLog log,
final List<Value> modifiedLinesCoverageDistribution,
final NavigableMap<Metric, Fraction> modifiedLinesCoverageDelta,
final NavigableMap<Metric, Fraction> coverageDelta, final StageResultHandler resultHandler,
final List<CoverageQualityGate> qualityGates) {
var statistics = new CoverageStatistics(rootNode.aggregateValues(), coverageDelta,
modifiedLinesCoverageDistribution, modifiedLinesCoverageDelta, List.of(), new TreeMap<>());
private QualityGateResult evaluateQualityGates(final List<CoverageQualityGate> qualityGates,
final CoverageStatistics statistics, final StageResultHandler resultHandler, final FilteredLog log) {
CoverageQualityGateEvaluator evaluator = new CoverageQualityGateEvaluator(qualityGates, statistics);
var qualityGateStatus = evaluator.evaluate();
if (qualityGateStatus.isInactive()) {
if (qualityGateStatus.isInactive() && qualityGates.isEmpty()) {
log.logInfo("No quality gates have been set - skipping");
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ Baseline.PROJECT_DELTA=Overall project (difference to reference job)
Baseline.MODIFIED_LINES=Modified code lines
Baseline.MODIFIED_LINES_DELTA=Modified code lines (difference to modified files)
Baseline.MODIFIED_FILES=Modified files
Baseline.MODIFIED_FILES_DELTA=Modified files (difference to overall project)
Baseline.MODIFIED_FILES_DELTA=Modified files (difference to reference job)
Baseline.INDIRECT=Indirect changes
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,30 @@
<dt>Overall project (difference to reference job) - PROJECT_DELTA</dt>
<dd>
Difference between the project coverages of the current build and the reference build. Teams can use this delta
value to ensure that the coverage will not decrease.
value to ensure that the overall project coverage will not decrease.
</dd>
<dt>Modified code lines - MODIFIED_LINES</dt>
<dt>Modified files - MODIFIED_FILES</dt>
<dd>
Coverage of the modified lines (e.g., within the modified lines of a pull or merge request)
will focus on new or modified code only.
Coverage of the modified files (e.g., within the files that have been touched in a pull or merge request)
will focus on new or modified files only.
</dd>
<dt>Modified code lines (difference to overall project) - MODIFIED_LINES_DELTA</dt>
<dt>Modified files (difference to reference job) - MODIFIED_FILES_DELTA</dt>
<dd>
Difference between the project coverage and the modified lines coverage of the current build. Teams can use this delta
value to ensure that the coverage of pull requests is better than the whole project coverage.
Difference between the file coverages of the current build and the reference build. Teams can use this delta
value to ensure that the overall coverage of all modified files will not decrease.
This also implies that the overall project coverage will not decrease, making this metric stricter
than the PROJECT_DELTA metric
</dd>
<dt>Modified files - MODIFIED_FILES</dt>
<dt>Modified code lines - MODIFIED_LINES</dt>
<dd>
Coverage of the modified files (e.g., within the files that have been touched in a pull or merge request)
Coverage of the modified lines (e.g., within the modified lines of a pull or merge request)
will focus on new or modified code only.
</dd>
<dt>Modified files (difference to overall project) - MODIFIED_FILES_DELTA</dt>
<dt>Modified code lines (difference to modified files) - MODIFIED_LINES_DELTA</dt>
<dd>
Difference between the project coverage and the modified files coverage of the current build. Teams can use this delta
value to ensure that the coverage of pull requests is better than the whole project coverage.
Difference between the coverages of the modified lines and the modified files of the current build.
Teams can use this delta value to ensure that the coverage of pull requests is better than the
existing coverage of the modified files.
</dd>
</dl>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ public static CoverageStatistics createOnlyProjectStatistics() {
private static List<Value> fillValues() {
var builder = new CoverageBuilder();
return List.of(
builder.setMetric(Metric.FILE).setCovered(3).setMissed(1).build(),
builder.setMetric(Metric.LINE).setCovered(2).setMissed(2).build(),
builder.setMetric(Metric.BRANCH).setCovered(9).setMissed(1).build(),
builder.withMetric(Metric.FILE).withCovered(3).withMissed(1).build(),
builder.withMetric(Metric.LINE).withCovered(2).withMissed(2).build(),
builder.withMetric(Metric.BRANCH).withCovered(9).withMissed(1).build(),
new CyclomaticComplexity(150),
new CyclomaticComplexity(15, Metric.COMPLEXITY_MAXIMUM),
new LinesOfCode(1000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ void shouldCreateChart() {
CoverageTrendChart trendChart = new CoverageTrendChart();

BuildResult<CoverageStatistics> smallLineCoverage = createResult(1,
new CoverageBuilder().setMetric(Metric.LINE).setCovered(1).setMissed(1).build(),
new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(3).setMissed(1).build());
new CoverageBuilder().withMetric(Metric.LINE).withCovered(1).withMissed(1).build(),
new CoverageBuilder().withMetric(Metric.BRANCH).withCovered(3).withMissed(1).build());

LinesChartModel lineCoverage = trendChart.create(Collections.singletonList(smallLineCoverage),
createConfiguration());
verifySeriesDetails(lineCoverage);

BuildResult<CoverageStatistics> smallBranchCoverage = createResult(1,
new CoverageBuilder().setMetric(Metric.LINE).setCovered(3).setMissed(1).build(),
new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(1).setMissed(1).build());
new CoverageBuilder().withMetric(Metric.LINE).withCovered(3).withMissed(1).build(),
new CoverageBuilder().withMetric(Metric.BRANCH).withCovered(1).withMissed(1).build());

LinesChartModel branchCoverage = trendChart.create(Collections.singletonList(smallBranchCoverage),
createConfiguration());
Expand Down Expand Up @@ -85,8 +85,8 @@ void shouldHaveTwoValuesForSingleBuild() {
CoverageSeriesBuilder builder = new CoverageSeriesBuilder();

BuildResult<CoverageStatistics> singleResult = createResult(1,
new CoverageBuilder().setMetric(Metric.LINE).setCovered(1).setMissed(1).build(),
new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(3).setMissed(1).build());
new CoverageBuilder().withMetric(Metric.LINE).withCovered(1).withMissed(1).build(),
new CoverageBuilder().withMetric(Metric.BRANCH).withCovered(3).withMissed(1).build());

LinesDataSet dataSet = builder.createDataSet(createConfiguration(), Collections.singletonList(singleResult));

Expand All @@ -106,11 +106,11 @@ void shouldHaveTwoValuesForTwoBuilds() {
CoverageSeriesBuilder builder = new CoverageSeriesBuilder();

BuildResult<CoverageStatistics> first = createResult(1,
new CoverageBuilder().setMetric(Metric.LINE).setCovered(1).setMissed(1).build(),
new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(3).setMissed(1).build());
new CoverageBuilder().withMetric(Metric.LINE).withCovered(1).withMissed(1).build(),
new CoverageBuilder().withMetric(Metric.BRANCH).withCovered(3).withMissed(1).build());
BuildResult<CoverageStatistics> second = createResult(2,
new CoverageBuilder().setMetric(Metric.LINE).setCovered(1).setMissed(3).build(),
new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(1).setMissed(3).build());
new CoverageBuilder().withMetric(Metric.LINE).withCovered(1).withMissed(3).build(),
new CoverageBuilder().withMetric(Metric.BRANCH).withCovered(1).withMissed(3).build());

LinesDataSet dataSet = builder.createDataSet(createConfiguration(), List.of(first, second));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ private CpsFlowDefinition createPipelineWithSourceCode(final SourceCodeRetention
private void verifySourceCodeInBuild(final String pathPrefix, final Run<?, ?> build, final String acuCobolParserSourceCodeSnippet,
final String pathUtilSourceCodeSnippet) {
List<CoverageBuildAction> actions = build.getActions(CoverageBuildAction.class);
var builder = new CoverageBuilder().setMetric(Metric.LINE).setMissed(0);
var builder = new CoverageBuilder().withMetric(Metric.LINE).withMissed(0);
assertThat(actions).hasSize(2).satisfiesExactly(
action -> {
assertThat(action.getAllValues(Baseline.PROJECT)).contains(builder.setCovered(8).build());
assertThat(action.getAllValues(Baseline.PROJECT)).contains(builder.withCovered(8).build());
var relativePath = getRelativePath(pathPrefix, ACU_COBOL_PARSER_SOURCE_FILE_PATH);
Optional<Node> fileNode = action.getResult().find(Metric.FILE, relativePath);
assertThat(fileNode).isNotEmpty().get()
Expand All @@ -188,7 +188,7 @@ private void verifySourceCodeInBuild(final String pathPrefix, final Run<?, ?> bu
.contains(acuCobolParserSourceCodeSnippet);
},
action -> {
assertThat(action.getAllValues(Baseline.PROJECT)).contains(builder.setCovered(43).build());
assertThat(action.getAllValues(Baseline.PROJECT)).contains(builder.withCovered(43).build());
var relativePath = getRelativePath(pathPrefix, PATH_UTIL_SOURCE_FILE_PATH);
Optional<Node> fileNode = action.getResult().find(Metric.FILE, relativePath);
assertThat(fileNode).isNotEmpty().get()
Expand Down
Loading

0 comments on commit aac84dd

Please sign in to comment.