From 114dd07d1edf52071e063cf4af93c34db1159c57 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 29 Mar 2023 14:54:07 +0200 Subject: [PATCH 1/8] Improve summary checks message. Add details about the covered and missed lines. --- .../steps/CoverageChecksPublisher.java | 108 ++++++++++++++++-- .../metrics/steps/Messages.properties | 2 - ...e-publisher-summary.checks-expected-result | 14 ++- 3 files changed, 110 insertions(+), 14 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java index 19bc2ea19..e10f1c42e 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java @@ -4,6 +4,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Optional; @@ -48,6 +50,7 @@ @SuppressWarnings("PMD.GodClass") class CoverageChecksPublisher { private static final ElementFormatter FORMATTER = new ElementFormatter(); + private static final int TITLE_HEADER_LEVEL = 4; private final CoverageBuildAction action; private final Node rootNode; @@ -137,11 +140,97 @@ private NavigableSet getMetricsForTitle() { } private String getSummary() { - return getOverallCoverageSummary() + "\n\n" + return getAnnotationSummary() + "\n\n" + + getOverallCoverageSummary() + "\n\n" + getQualityGatesSummary() + "\n\n" + getProjectMetricsSummary(rootNode); } + private String getAnnotationSummary() { + if (rootNode.hasModifiedLines()) { + var filteredRoot = rootNode.filterByModifiedLines(); + var modifiedFiles = filteredRoot.getAllFileNodes(); + + var summary = new StringBuilder("Modified lines summary:\n"); + + createTotalLinesSummary(modifiedFiles, summary); + createLineCoverageSummary(modifiedFiles, summary); + createBranchCoverageSummary(filteredRoot, modifiedFiles, summary); + createMutationCoverageSummary(filteredRoot, modifiedFiles, summary); + + return summary.toString(); + } + return StringUtils.EMPTY; + } + + private void createTotalLinesSummary(final List modifiedFiles, final StringBuilder summary) { + var total = modifiedFiles.stream().map(FileNode::getModifiedLines).map(Set::size).count(); + if (total == 1) { + summary.append("- 1 line has been modified"); + } + else { + summary.append(String.format("- %d lines have been modified", total)); + } + summary.append('\n'); + } + + private void createLineCoverageSummary(final List modifiedFiles, final StringBuilder summary) { + var missed = modifiedFiles.stream().map(FileNode::getMissedLines).map(Set::size).count(); + if (missed == 0) { + summary.append("- all lines are covered"); + } + else if (missed == 1) { + summary.append("- 1 line is not covered"); + } + else { + summary.append(String.format("- %d lines are not covered", missed)); + } + summary.append('\n'); + } + + private void createBranchCoverageSummary(final Node filteredRoot, final List modifiedFiles, final StringBuilder summary) { + if (filteredRoot.containsMetric(Metric.BRANCH)) { + var partiallyCovered = modifiedFiles.stream() + .map(FileNode::getPartiallyCoveredLines) + .map(Map::size) + .count(); + if (partiallyCovered == 1) { + summary.append("- 1 line is covered only partially"); + } + else { + summary.append(String.format("- %d lines are covered only partially", partiallyCovered)); + } + summary.append('\n'); + } + } + + private void createMutationCoverageSummary(final Node filteredRoot, final List modifiedFiles, final StringBuilder summary) { + if (filteredRoot.containsMetric(Metric.MUTATION)) { + var survived = modifiedFiles.stream() + .map(FileNode::getSurvivedMutations) + .map(Map::entrySet) + .flatMap(Collection::stream) + .map(Entry::getValue) + .count(); + var mutations = modifiedFiles.stream().map(FileNode::getMutations).mapToLong(Collection::size).sum(); + if (survived == 0) { + if (mutations == 1) { + summary.append("- 1 mutation has been killed"); + } + else { + summary.append(String.format("- all %d mutations have been killed", mutations)); + } + } + if (survived == 1) { + summary.append(String.format("- 1 mutation survived (of %d)", mutations)); + } + else { + summary.append(String.format("- %d mutations survived (of %d)", survived, mutations)); + } + summary.append('\n'); + } + } + private List getAnnotations() { if (annotationScope == ChecksAnnotationScope.SKIP) { return List.of(); @@ -228,7 +317,7 @@ private List getBaselines() { } private String getOverallCoverageSummary() { - StringBuilder description = new StringBuilder(getSectionHeader(2, Messages.Checks_Summary())); + StringBuilder description = new StringBuilder(getSectionHeader(TITLE_HEADER_LEVEL, "Overview by baseline")); for (Baseline baseline : getBaselines()) { if (action.hasBaselineResult(baseline)) { @@ -240,7 +329,7 @@ private String getOverallCoverageSummary() { if (action.hasDelta(baseline, value.getMetric())) { display += String.format(" - Delta: %s", action.formatDelta(baseline, value.getMetric())); } - description.append(getBulletListItem(2, display)); + description.append(getBulletListItem(TITLE_HEADER_LEVEL, display)); } } } @@ -254,12 +343,17 @@ private String getOverallCoverageSummary() { */ // TODO: expand with summary of status of each defined quality gate private String getQualityGatesSummary() { - return getSectionHeader(2, - Messages.Checks_QualityGates(action.getQualityGateResult().getOverallStatus().name())); + String summary = getSectionHeader(TITLE_HEADER_LEVEL, "Quality Gates Summary"); + var qualityGateResult = action.getQualityGateResult(); + if (qualityGateResult.isInactive()) { + return summary + "No active quality gates."; + } + return summary + "Overall result: " + qualityGateResult.getOverallStatus().getDescription() + "\n" + + qualityGateResult.getMessages().stream().collect(Collectors.joining("\n", "- ", "")); } private String getProjectMetricsSummary(final Node result) { - String sectionHeader = getSectionHeader(2, Messages.Checks_ProjectOverview()); + String sectionHeader = getSectionHeader(TITLE_HEADER_LEVEL, "Project coverage details"); List coverageDisplayNames = FORMATTER.getSortedCoverageDisplayNames(); String header = formatRow(coverageDisplayNames); @@ -352,7 +446,7 @@ private List getTableSeparators(final ColumnAlignment alignment, final i } private String getBulletListItem(final int level, final String text) { - int whitespaces = (level - 1) * 2; + int whitespaces = (level - 1) * TITLE_HEADER_LEVEL; return String.join("", Collections.nCopies(whitespaces, " ")) + "* " + text + "\n"; } diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/Messages.properties index 68f0622c7..cb9351e53 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/Messages.properties @@ -33,8 +33,6 @@ Column.ComplexityDensity=Complexity / LOC MessagesViewModel.Title=Code Coverage -Checks.Summary=Coverage Report Overview -Checks.QualityGates=Quality Gates Summary - {0} Checks.ProjectOverview=Project Coverage Summary Checks.Annotation.Title=Missing Coverage Checks.Annotation.Message.SingleLine=Changed line #L{0} is not covered by tests diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result index fde6afb9b..beb45ff48 100644 --- a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result @@ -1,4 +1,9 @@ -## Coverage Report Overview +Modified lines summary: +- 1 line has been modified +- 1 line is not covered +- 1 line is covered only partially + +#### Overview by baseline * **[Overall project (difference to reference job)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#overview)** * Line Coverage: 91.02% (294/323) - Delta: +50.00% @@ -12,12 +17,11 @@ * **[Indirect changes](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#indirectCoverage)** * Line Coverage: 50.00% (1/2) +#### Quality Gates Summary -## Quality Gates Summary - INACTIVE - - +No active quality gates. -## Project Coverage Summary +#### Project coverage details |Container Coverage|Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage| |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| From b94ef6b02cc15cf57464098e188daad69b9d1982 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 29 Mar 2023 15:07:21 +0200 Subject: [PATCH 2/8] Move details from summary to details property. --- .../steps/CoverageChecksPublisher.java | 31 ++++++++++++------- .../steps/CoverageChecksPublisherTest.java | 6 +++- ...e-publisher-summary.checks-expected-result | 7 ----- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java index e10f1c42e..2a671683b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java @@ -12,6 +12,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.stream.Collector; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -51,6 +52,7 @@ class CoverageChecksPublisher { private static final ElementFormatter FORMATTER = new ElementFormatter(); private static final int TITLE_HEADER_LEVEL = 4; + private static final char NEW_LINE = '\n'; private final CoverageBuildAction action; private final Node rootNode; @@ -89,6 +91,7 @@ ChecksDetails extractChecksDetails() { var output = new ChecksOutputBuilder() .withTitle(getChecksTitle()) .withSummary(getSummary()) + .withText(getProjectMetricsSummary(rootNode)) .withAnnotations(getAnnotations()) .build(); @@ -140,10 +143,9 @@ private NavigableSet getMetricsForTitle() { } private String getSummary() { - return getAnnotationSummary() + "\n\n" - + getOverallCoverageSummary() + "\n\n" - + getQualityGatesSummary() + "\n\n" - + getProjectMetricsSummary(rootNode); + return getAnnotationSummary() + + getOverallCoverageSummary() + + getQualityGatesSummary(); } private String getAnnotationSummary() { @@ -171,7 +173,7 @@ private void createTotalLinesSummary(final List modifiedFiles, final S else { summary.append(String.format("- %d lines have been modified", total)); } - summary.append('\n'); + summary.append(NEW_LINE); } private void createLineCoverageSummary(final List modifiedFiles, final StringBuilder summary) { @@ -185,7 +187,7 @@ else if (missed == 1) { else { summary.append(String.format("- %d lines are not covered", missed)); } - summary.append('\n'); + summary.append(NEW_LINE); } private void createBranchCoverageSummary(final Node filteredRoot, final List modifiedFiles, final StringBuilder summary) { @@ -200,7 +202,7 @@ private void createBranchCoverageSummary(final Node filteredRoot, final List asSeparateLines() { + return Collectors.joining("\n", "- ", "\n"); } private String getProjectMetricsSummary(final Node result) { @@ -462,7 +469,7 @@ private String formatRow(final Collection columns) { if (!columns.isEmpty()) { row.append('|'); } - row.append('\n'); + row.append(NEW_LINE); return row.toString(); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java index 51e1d8180..74cbf9a71 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java @@ -93,7 +93,11 @@ private void assertThatDetailsAreCorrect(final ChecksDetails checkDetails, final assertThat(output.getTitle()).isPresent() .get() .isEqualTo("Line Coverage: 50.00% (+50.00%)"); - assertThat(output.getText()).isEmpty(); + assertThat(output.getText()).contains("#### Project coverage details\n\n" + + "|Container Coverage|Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage|\n" + + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n" + + "|:white_check_mark: **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)|\n" + + "|:chart_with_upwards_trend: **Overall project (difference to reference job)**|-|+20.00% :arrow_up:|-|-|-|-|+50.00% :arrow_up:|-|-|\n"); assertChecksAnnotations(output, expectedAnnotations); assertSummary(output); }); diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result index beb45ff48..896556689 100644 --- a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-summary.checks-expected-result @@ -20,10 +20,3 @@ Modified lines summary: #### Quality Gates Summary No active quality gates. - -#### Project coverage details - -|Container Coverage|Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage| -|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -|:white_check_mark: **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)| -|:chart_with_upwards_trend: **Overall project (difference to reference job)**|-|+20.00% :arrow_up:|-|-|-|-|+50.00% :arrow_up:|-|-| From b94eeb9637596936ec9cba08d1a016011bc12398 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 29 Mar 2023 15:20:38 +0200 Subject: [PATCH 3/8] Remove cell (0,0) text. --- .../plugins/coverage/metrics/steps/CoverageChecksPublisher.java | 1 + .../coverage/metrics/steps/CoverageChecksPublisherTest.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java index 2a671683b..5e74ec5ec 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java @@ -363,6 +363,7 @@ private String getProjectMetricsSummary(final Node result) { String sectionHeader = getSectionHeader(TITLE_HEADER_LEVEL, "Project coverage details"); List coverageDisplayNames = FORMATTER.getSortedCoverageDisplayNames(); + coverageDisplayNames.set(0, ""); String header = formatRow(coverageDisplayNames); String headerSeparator = formatRow( getTableSeparators(ColumnAlignment.CENTER, coverageDisplayNames.size())); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java index 74cbf9a71..c95e74ae2 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java @@ -94,7 +94,7 @@ private void assertThatDetailsAreCorrect(final ChecksDetails checkDetails, final .get() .isEqualTo("Line Coverage: 50.00% (+50.00%)"); assertThat(output.getText()).contains("#### Project coverage details\n\n" - + "|Container Coverage|Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage|\n" + + "||Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage|\n" + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n" + "|:white_check_mark: **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)|\n" + "|:chart_with_upwards_trend: **Overall project (difference to reference job)**|-|+20.00% :arrow_up:|-|-|-|-|+50.00% :arrow_up:|-|-|\n"); From acda07f05e8abf37aed5ee128fbde3592d81dc6c Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 29 Mar 2023 16:45:45 +0200 Subject: [PATCH 4/8] Show all baseline results in details. --- .../metrics/model/ElementFormatter.java | 2 + .../metrics/steps/CoverageBuildAction.java | 3 + .../steps/CoverageChecksPublisher.java | 134 ++++++++---------- .../steps/CoverageChecksPublisherTest.java | 10 +- ...e-publisher-details.checks-expected-result | 11 ++ 5 files changed, 75 insertions(+), 85 deletions(-) create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-details.checks-expected-result diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java index db50e1f85..1622bcdd5 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java @@ -498,6 +498,8 @@ public String getDisplayName(final Baseline baseline) { return Messages.Baseline_MODIFIED_LINES_DELTA(); case MODIFIED_FILES_DELTA: return Messages.Baseline_MODIFIED_FILES_DELTA(); + case INDIRECT: + return Messages.Baseline_INDIRECT(); default: throw new NoSuchElementException("No display name found for baseline " + baseline); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageBuildAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageBuildAction.java index 96585e9bb..a2e920d54 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageBuildAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageBuildAction.java @@ -274,6 +274,9 @@ public Baseline getDeltaBaseline(final Baseline baseline) { if (baseline == Baseline.MODIFIED_FILES) { return Baseline.MODIFIED_FILES_DELTA; } + if (baseline == Baseline.INDIRECT) { + return Baseline.INDIRECT; + } throw new NoSuchElementException("No delta baseline for this baseline: " + baseline); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java index 5e74ec5ec..0183b0737 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java @@ -6,26 +6,22 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Optional; import java.util.Set; -import java.util.TreeMap; import java.util.TreeSet; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.Fraction; -import edu.hm.hafner.coverage.Coverage; import edu.hm.hafner.coverage.FileNode; import edu.hm.hafner.coverage.Metric; import edu.hm.hafner.coverage.Node; import edu.hm.hafner.coverage.Value; import edu.hm.hafner.util.VisibleForTesting; -import hudson.Functions; import hudson.model.TaskListener; import io.jenkins.plugins.checks.api.ChecksAnnotation; @@ -53,6 +49,7 @@ class CoverageChecksPublisher { private static final ElementFormatter FORMATTER = new ElementFormatter(); private static final int TITLE_HEADER_LEVEL = 4; private static final char NEW_LINE = '\n'; + private static final String COLUMN = "|"; private final CoverageBuildAction action; private final Node rootNode; @@ -91,7 +88,7 @@ ChecksDetails extractChecksDetails() { var output = new ChecksOutputBuilder() .withTitle(getChecksTitle()) .withSummary(getSummary()) - .withText(getProjectMetricsSummary(rootNode)) + .withText(getProjectMetricsSummary()) .withAnnotations(getAnnotations()) .build(); @@ -190,7 +187,8 @@ else if (missed == 1) { summary.append(NEW_LINE); } - private void createBranchCoverageSummary(final Node filteredRoot, final List modifiedFiles, final StringBuilder summary) { + private void createBranchCoverageSummary(final Node filteredRoot, final List modifiedFiles, + final StringBuilder summary) { if (filteredRoot.containsMetric(Metric.BRANCH)) { var partiallyCovered = modifiedFiles.stream() .map(FileNode::getPartiallyCoveredLines) @@ -206,7 +204,8 @@ private void createBranchCoverageSummary(final Node filteredRoot, final List modifiedFiles, final StringBuilder summary) { + private void createMutationCoverageSummary(final Node filteredRoot, final List modifiedFiles, + final StringBuilder summary) { if (filteredRoot.containsMetric(Metric.MUTATION)) { var survived = modifiedFiles.stream() .map(FileNode::getSurvivedMutations) @@ -359,33 +358,52 @@ private String getQualityGatesSummary() { return Collectors.joining("\n", "- ", "\n"); } - private String getProjectMetricsSummary(final Node result) { - String sectionHeader = getSectionHeader(TITLE_HEADER_LEVEL, "Project coverage details"); + private String getProjectMetricsSummary() { + var builder = new StringBuilder(getSectionHeader(TITLE_HEADER_LEVEL, "Project coverage details")); + builder.append(COLUMN); + builder.append(COLUMN); + builder.append(getMetricStream() + .map(FORMATTER::getDisplayName) + .collect(asColumn())); + builder.append(COLUMN); + builder.append(":---:"); + builder.append(COLUMN); + builder.append(getMetricStream() + .map(i -> ":---:") + .collect(asColumn())); + for (Baseline baseline : action.getBaselines()) { + if (action.hasBaselineResult(baseline)) { + builder.append(String.format("%s **%s**|", Icon.FEET.markdown, + FORMATTER.getDisplayName(baseline))); + builder.append(getMetricStream() + .map(metric -> action.formatValue(baseline, metric)) + .collect(asColumn())); + + var deltaBaseline = action.getDeltaBaseline(baseline); + if (deltaBaseline != baseline) { + builder.append(String.format("%s **%s**|", Icon.CHART_UPWARDS_TREND.markdown, + FORMATTER.getDisplayName(deltaBaseline))); + builder.append(getMetricStream() + .map(metric -> getFormatDelta(baseline, metric)) + .collect(asColumn())); + } + } + } - List coverageDisplayNames = FORMATTER.getSortedCoverageDisplayNames(); - coverageDisplayNames.set(0, ""); - String header = formatRow(coverageDisplayNames); - String headerSeparator = formatRow( - getTableSeparators(ColumnAlignment.CENTER, coverageDisplayNames.size())); + return builder.toString(); + } - String projectCoverageName = String.format("|%s **%s**", Icon.WHITE_CHECK_MARK.markdown, - FORMATTER.getDisplayName(Baseline.PROJECT)); - List projectCoverage = FORMATTER.getFormattedValues(FORMATTER.getSortedCoverageValues(result), - Functions.getCurrentLocale()); - String projectCoverageRow = projectCoverageName + formatRow(projectCoverage); + private String getFormatDelta(final Baseline baseline, final Metric metric) { + var delta = action.formatDelta(baseline, metric); + return delta + getTrendIcon(delta); + } - String projectCoverageDeltaName = String.format("|%s **%s**", Icon.CHART_UPWARDS_TREND.markdown, - FORMATTER.getDisplayName(Baseline.PROJECT_DELTA)); - Collection projectCoverageDelta = formatCoverageDelta(Metric.getCoverageMetrics(), - action.getAllDeltas(Baseline.PROJECT_DELTA)); - String projectCoverageDeltaRow = - projectCoverageDeltaName + formatRow(projectCoverageDelta); + private Stream getMetricStream() { + return Metric.getCoverageMetrics().stream().skip(1); + } - return sectionHeader - + header - + headerSeparator - + projectCoverageRow - + projectCoverageDeltaRow; + private Collector asColumn() { + return Collectors.joining(COLUMN, "", "\n"); } private String formatText(final TextFormat format, final String text) { @@ -399,58 +417,17 @@ private String formatText(final TextFormat format, final String text) { } } - /** - * Formats the passed delta computation to a collection of its display representations, which is sorted by the - * metric ordinal. Also, a collection of required metrics is passed. This is used to fill not existent metrics which - * are required for the representation. Coverage deltas might not be existent if the reference does not contain a - * reference value of the metric. - * - * @param requiredMetrics - * The metrics which should be displayed - * @param deltas - * The delta calculation mapped by their metric - * @return the delta for each metric to be shown in the MD file - */ - private Collection formatCoverageDelta(final Collection requiredMetrics, - final NavigableMap deltas) { - var coverageDelta = new TreeMap(); - for (Metric metric : requiredMetrics) { - if (deltas.containsKey(metric)) { - var coverage = deltas.get(metric); - coverageDelta.putIfAbsent(metric, - FORMATTER.formatDelta(coverage, metric, Functions.getCurrentLocale()) - + getTrendIcon(coverage.doubleValue())); - } - else { - coverageDelta.putIfAbsent(metric, - FORMATTER.formatPercentage(Coverage.nullObject(metric), Functions.getCurrentLocale())); - } - } - return coverageDelta.values(); - } - - private String getTrendIcon(final double trend) { - if (trend > 0) { + private String getTrendIcon(final String trend) { + if (trend.startsWith("+")) { return " " + Icon.ARROW_UP.markdown; } - else if (trend < 0) { + else if (trend.startsWith("-")) { return " " + Icon.ARROW_DOWN.markdown; } - else { - return " " + Icon.ARROW_RIGHT.markdown; - } - } - - private List getTableSeparators(final ColumnAlignment alignment, final int count) { - switch (alignment) { - case LEFT: - return Collections.nCopies(count, ":---"); - case RIGHT: - return Collections.nCopies(count, "---:"); - case CENTER: - default: - return Collections.nCopies(count, ":---:"); + else if (trend.startsWith("n/a")) { + return StringUtils.EMPTY; } + return " " + Icon.ARROW_RIGHT.markdown; } private String getBulletListItem(final int level, final String text) { @@ -498,6 +475,7 @@ private enum ColumnAlignment { } private enum Icon { + FEET(":feet:"), WHITE_CHECK_MARK(":white_check_mark:"), CHART_UPWARDS_TREND(":chart_with_upwards_trend:"), ARROW_UP(":arrow_up:"), diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java index c95e74ae2..b9f4ddcf2 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java @@ -1,7 +1,6 @@ package io.jenkins.plugins.coverage.metrics.steps; import java.io.IOException; -import java.nio.file.Files; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -93,18 +92,15 @@ private void assertThatDetailsAreCorrect(final ChecksDetails checkDetails, final assertThat(output.getTitle()).isPresent() .get() .isEqualTo("Line Coverage: 50.00% (+50.00%)"); - assertThat(output.getText()).contains("#### Project coverage details\n\n" - + "||Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage|\n" - + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n" - + "|:white_check_mark: **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)|\n" - + "|:chart_with_upwards_trend: **Overall project (difference to reference job)**|-|+20.00% :arrow_up:|-|-|-|-|+50.00% :arrow_up:|-|-|\n"); + var expectedDetails = toString("coverage-publisher-details.checks-expected-result"); + assertThat(output.getText()).contains(expectedDetails); assertChecksAnnotations(output, expectedAnnotations); assertSummary(output); }); } private void assertSummary(final ChecksOutput checksOutput) throws IOException { - var expectedContent = Files.readString(getResourceAsFile("coverage-publisher-summary.checks-expected-result")); + var expectedContent = toString("coverage-publisher-summary.checks-expected-result"); assertThat(checksOutput.getSummary()).isPresent() .get() .asString().isEqualToNormalizingWhitespace(expectedContent); diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-details.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-details.checks-expected-result new file mode 100644 index 000000000..b8352bee7 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-details.checks-expected-result @@ -0,0 +1,11 @@ +#### Project coverage details + +||Module Coverage|Package Coverage|File Coverage|Class Coverage|Method Coverage|Line Coverage|Branch Coverage|Instruction Coverage +|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---: +: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) +:chart_with_upwards_trend: **Overall project (difference to reference job)**|+20.00% :arrow_up:|n/a|n/a|n/a|n/a|+50.00% :arrow_up:|n/a|n/a +:feet: **Modified files**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a +: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 +:feet: **Modified code lines**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a +: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 +:feet: **Indirect changes**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a From c5ff4219a70b7a5be6a326e56828170515ddd28d Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 29 Mar 2023 19:46:19 +0200 Subject: [PATCH 5/8] Normalize whitespace. --- .../coverage/metrics/steps/CoverageChecksPublisherTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java index b9f4ddcf2..bdd2b6753 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java @@ -93,7 +93,7 @@ private void assertThatDetailsAreCorrect(final ChecksDetails checkDetails, final .get() .isEqualTo("Line Coverage: 50.00% (+50.00%)"); var expectedDetails = toString("coverage-publisher-details.checks-expected-result"); - assertThat(output.getText()).contains(expectedDetails); + assertThat(output.getText()).isPresent().get().asString().isEqualToNormalizingWhitespace(expectedDetails); assertChecksAnnotations(output, expectedAnnotations); assertSummary(output); }); From 695830f4268cfaa3c8eed33a8ede74f76b8c5b36 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 29 Mar 2023 19:57:13 +0200 Subject: [PATCH 6/8] Add test for quality gates. --- .../steps/CoverageChecksPublisher.java | 19 +++------- .../steps/CoverageChecksPublisherTest.java | 37 ++++++++++++++++++- .../CoverageQualityGateEvaluatorTest.java | 33 ++++++++++------- 3 files changed, 60 insertions(+), 29 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java index 0183b0737..faa3fefbe 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisher.java @@ -351,11 +351,14 @@ private String getQualityGatesSummary() { } return summary + "Overall result: " + qualityGateResult.getOverallStatus().getDescription() + "\n" - + qualityGateResult.getMessages().stream().collect(asSeparateLines()); + + qualityGateResult.getMessages().stream() + .map(s -> s.replaceAll("-> ", "")) + .map(s -> s.replaceAll("[\\[\\]]", "")) + .collect(asSeparateLines()); } private Collector asSeparateLines() { - return Collectors.joining("\n", "- ", "\n"); + return Collectors.joining("\n- ", "- ", "\n"); } private String getProjectMetricsSummary() { @@ -439,18 +442,6 @@ private String getUrlText(final String text, final String url) { return String.format("[%s](%s)", text, url); } - private String formatRow(final Collection columns) { - StringBuilder row = new StringBuilder(); - for (Object column : columns) { - row.append(String.format("|%s", column)); - } - if (!columns.isEmpty()) { - row.append('|'); - } - row.append(NEW_LINE); - return row.toString(); - } - private String getSectionHeader(final int level, final String text) { return String.join("", Collections.nCopies(level, "#")) + " " + text + "\n\n"; } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java index bdd2b6753..bbc995752 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java @@ -40,6 +40,37 @@ class CoverageChecksPublisherTest extends AbstractCoverageTest { private static final String REPORT_NAME = "Name"; private static final int ANNOTATIONS_COUNT_FOR_MODIFIED = 3; + @Test + void shouldShowQualityGateDetails() { + var result = readJacocoResult("jacoco-codingstyle.xml"); + + var publisher = new CoverageChecksPublisher(createActionWithoutDelta(result, + CoverageQualityGateEvaluatorTest.createQualityGateResult()), result, REPORT_NAME, + ChecksAnnotationScope.SKIP, createJenkins()); + + var checkDetails = publisher.extractChecksDetails(); + assertThat(checkDetails.getOutput()).isPresent().get().satisfies(output -> { + assertThat(output.getSummary()).isPresent() + .get() + .asString() + .containsIgnoringWhitespaces("#### Quality Gates Summary\n" + + "\n" + + "Overall result: Unstable\n" + + "- Overall project - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)\n" + + "- Overall project - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)\n" + + "- Modified code lines - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)\n" + + "- Modified code lines - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)\n" + + "- Modified files - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)\n" + + "- Modified files - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)\n" + + "- Overall project (difference to reference job) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)\n" + + "- Overall project (difference to reference job) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)\n" + + "- Modified code lines (difference to modified files) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)\n" + + "- Modified code lines (difference to modified files) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)\n" + + "- Modified files (difference to overall project) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)\n" + + "- Modified files (difference to overall project) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)"); + }); + } + @Test void shouldShowProjectBaselineForJaCoCo() { var result = readJacocoResult("jacoco-codingstyle.xml"); @@ -170,11 +201,15 @@ private CoverageBuildAction createCoverageBuildAction(final Node result) { } private CoverageBuildAction createActionWithoutDelta(final Node result) { + return createActionWithoutDelta(result, new QualityGateResult()); + } + + CoverageBuildAction createActionWithoutDelta(final Node result, final QualityGateResult qualityGateResult) { var run = mock(Run.class); when(run.getUrl()).thenReturn(BUILD_LINK); return new CoverageBuildAction(run, COVERAGE_ID, REPORT_NAME, StringUtils.EMPTY, result, - new QualityGateResult(), null, "refId", + qualityGateResult, null, "refId", new TreeMap<>(), List.of(), new TreeMap<>(), List.of(), new TreeMap<>(), List.of(), false); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java index 1af65e2fc..bb65ed14a 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java @@ -92,6 +92,24 @@ void shouldSkipIfValueNotDefined() { @Test void shouldReportUnstableIfBelowThreshold() { + QualityGateResult result = createQualityGateResult(); + + assertThat(result).hasOverallStatus(QualityGateStatus.WARNING).isNotSuccessful().isNotInactive().hasMessages( + "-> [Overall project - File Coverage]: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)", + "-> [Overall project - Line Coverage]: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)", + "-> [Modified code lines - File Coverage]: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)", + "-> [Modified code lines - Line Coverage]: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)", + "-> [Modified files - File Coverage]: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)", + "-> [Modified files - Line Coverage]: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)", + "-> [Overall project (difference to reference job) - File Coverage]: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)", + "-> [Overall project (difference to reference job) - Line Coverage]: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)", + "-> [Modified code lines (difference to modified files) - File Coverage]: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)", + "-> [Modified code lines (difference to modified files) - Line Coverage]: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)", + "-> [Modified files (difference to overall project) - File Coverage]: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)", + "-> [Modified files (difference to overall project) - Line Coverage]: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)"); + } + + static QualityGateResult createQualityGateResult() { Collection qualityGates = new ArrayList<>(); qualityGates.add(new CoverageQualityGate(76.0, Metric.FILE, Baseline.PROJECT, QualityGateCriticality.UNSTABLE)); @@ -111,20 +129,7 @@ void shouldReportUnstableIfBelowThreshold() { CoverageQualityGateEvaluator evaluator = new CoverageQualityGateEvaluator(qualityGates, createStatistics()); QualityGateResult result = evaluator.evaluate(); - - assertThat(result).hasOverallStatus(QualityGateStatus.WARNING).isNotSuccessful().isNotInactive().hasMessages( - "-> [Overall project - File Coverage]: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)", - "-> [Overall project - Line Coverage]: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)", - "-> [Modified code lines - File Coverage]: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)", - "-> [Modified code lines - Line Coverage]: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)", - "-> [Modified files - File Coverage]: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)", - "-> [Modified files - Line Coverage]: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)", - "-> [Overall project (difference to reference job) - File Coverage]: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)", - "-> [Overall project (difference to reference job) - Line Coverage]: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)", - "-> [Modified code lines (difference to modified files) - File Coverage]: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)", - "-> [Modified code lines (difference to modified files) - Line Coverage]: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)", - "-> [Modified files (difference to overall project) - File Coverage]: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)", - "-> [Modified files (difference to overall project) - Line Coverage]: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)"); + return result; } @Test From e8e5d0e122ee1ee9a66e0898ee87de1e516de156 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 29 Mar 2023 22:28:47 +0200 Subject: [PATCH 7/8] Fix two warnings. --- .../steps/CoverageChecksPublisherTest.java | 17 ++--------------- .../steps/CoverageQualityGateEvaluatorTest.java | 3 +-- ...ublisher-quality-gate.checks-expected-result | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 17 deletions(-) create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-quality-gate.checks-expected-result diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java index bbc995752..9fa656e9d 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageChecksPublisherTest.java @@ -49,25 +49,12 @@ void shouldShowQualityGateDetails() { ChecksAnnotationScope.SKIP, createJenkins()); var checkDetails = publisher.extractChecksDetails(); + var expectedSummary = toString("coverage-publisher-quality-gate.checks-expected-result"); assertThat(checkDetails.getOutput()).isPresent().get().satisfies(output -> { assertThat(output.getSummary()).isPresent() .get() .asString() - .containsIgnoringWhitespaces("#### Quality Gates Summary\n" - + "\n" - + "Overall result: Unstable\n" - + "- Overall project - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)\n" - + "- Overall project - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)\n" - + "- Modified code lines - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)\n" - + "- Modified code lines - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)\n" - + "- Modified files - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00)\n" - + "- Modified files - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00)\n" - + "- Overall project (difference to reference job) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)\n" - + "- Overall project (difference to reference job) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)\n" - + "- Modified code lines (difference to modified files) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)\n" - + "- Modified code lines (difference to modified files) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)\n" - + "- Modified files (difference to overall project) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00)\n" - + "- Modified files (difference to overall project) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00)"); + .containsIgnoringWhitespaces(expectedSummary); }); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java index bb65ed14a..3ded4fed5 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGateEvaluatorTest.java @@ -128,8 +128,7 @@ static QualityGateResult createQualityGateResult() { qualityGates.add(new CoverageQualityGate(minimum, Metric.LINE, Baseline.MODIFIED_FILES_DELTA, QualityGateCriticality.UNSTABLE)); CoverageQualityGateEvaluator evaluator = new CoverageQualityGateEvaluator(qualityGates, createStatistics()); - QualityGateResult result = evaluator.evaluate(); - return result; + return evaluator.evaluate(); } @Test diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-quality-gate.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-quality-gate.checks-expected-result new file mode 100644 index 000000000..6c8c5c963 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/coverage-publisher-quality-gate.checks-expected-result @@ -0,0 +1,15 @@ +#### Quality Gates Summary + +Overall result: Unstable +- Overall project - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Overall project - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Modified code lines - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Modified code lines - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Modified files - File Coverage: ≪Unstable≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Modified files - Line Coverage: ≪Unstable≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Overall project (difference to reference job) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Overall project (difference to reference job) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00) +- Modified code lines (difference to modified files) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Modified code lines (difference to modified files) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00) +- Modified files (difference to overall project) - File Coverage: ≪Unstable≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Modified files (difference to overall project) - Line Coverage: ≪Unstable≫ - (Actual value: +5.00%, Quality gate: 10.00) From 3ef6f2907c258f1aff3c8f987ddac567e454840d Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Thu, 30 Mar 2023 22:13:54 +0200 Subject: [PATCH 8/8] Move non-formatting method back to model. --- .../metrics/model/ElementFormatter.java | 19 ------------------- .../metrics/steps/CoverageViewModel.java | 11 ++++++++++- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java index 1622bcdd5..7c2d77b07 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/model/ElementFormatter.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.Locale; import java.util.NoSuchElementException; -import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -15,7 +14,6 @@ import edu.hm.hafner.coverage.FractionValue; import edu.hm.hafner.coverage.IntegerValue; import edu.hm.hafner.coverage.Metric; -import edu.hm.hafner.coverage.Node; import edu.hm.hafner.coverage.Percentage; import edu.hm.hafner.coverage.Value; @@ -417,23 +415,6 @@ public List getFormattedValues(final Stream values, fin return values.map(value -> formatDetails(value, locale)).collect(Collectors.toList()); } - /** - * Returns a stream of {@link Coverage} values for the given root node sorted by the metric ordinal. - * - * @param coverage - * The coverage root node - * - * @return a stream containing the existent coverage values - */ - public Stream getSortedCoverageValues(final Node coverage) { - return Metric.getCoverageMetrics() - .stream() - .map(m -> m.getValueFor(coverage)) - .flatMap(Optional::stream) - .filter(value -> value instanceof Coverage) - .map(Coverage.class::cast); - } - /** * Returns a localized human-readable label for the specified metric. * diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageViewModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageViewModel.java index 659b72a92..adcfad6a1 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageViewModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageViewModel.java @@ -487,10 +487,19 @@ public List getMetrics() { } private Stream sortCoverages() { - return ELEMENT_FORMATTER.getSortedCoverageValues(coverage) + return getSortedCoverageValues() .filter(c -> c.getTotal() > 1); // ignore elements that have a total of 1 } + private Stream getSortedCoverageValues() { + return Metric.getCoverageMetrics() + .stream() + .map(m -> m.getValueFor(coverage)) + .flatMap(Optional::stream) + .filter(value -> value instanceof Coverage) + .map(Coverage.class::cast); + } + public List getCovered() { return getCoverageCounter(Coverage::getCovered); }