From bb12dae7a8ce9f1aec03ecf4db17c34ca8a87c21 Mon Sep 17 00:00:00 2001 From: Tim Schneider Date: Wed, 26 Jun 2024 10:39:39 -0400 Subject: [PATCH] Add coverage metrics from VectorCAST (#153) Add coverage metrics from VectorCAST. --- README.md | 2 + plugin/pom.xml | 2 +- .../metrics/charts/CoverageSeriesBuilder.java | 7 + .../metrics/charts/CoverageTrendChart.java | 33 +- .../metrics/model/ElementFormatter.java | 10 + .../metrics/source/CoverageSourcePrinter.java | 12 +- .../metrics/source/SourceCodePainter.java | 7 + .../source/VectorCastSourcePrinter.java | 470 +++++++++++ .../metrics/steps/CoverageBuildAction.java | 3 +- .../steps/CoverageChecksPublisher.java | 60 +- .../metrics/steps/CoverageTableModel.java | 36 +- .../coverage/metrics/steps/CoverageTool.java | 3 + .../metrics/steps/CoverageViewModel.java | 12 +- .../metrics/model/Messages.properties | 4 + .../CoverageMetricColumn/help-metric.html | 4 + .../CoverageQualityGate/help-metric.html | 4 + .../metrics/steps/Messages.properties | 4 + .../metrics/AbstractCoverageTest.java | 5 + .../steps/CoverageChecksPublisherTest.java | 113 ++- .../metrics/steps/CoveragePluginITest.java | 398 ++++++++- .../metrics/steps/CoverageRecorderITest.java | 90 -- ...ality-gate-overview.checks-expected-result | 26 + ...lisher-quality-gate.checks-expected-result | 15 + ...blisher-s+b-details.checks-expected-result | 11 + ...lisher-s+b-overview.checks-expected-result | 11 + ...-s+mcdc+fcc-details.checks-expected-result | 11 + ...s+mcdc+fcc-overview.checks-expected-result | 14 + ...sher-s+mcdc-details.checks-expected-result | 11 + ...her-s+mcdc-overview.checks-expected-result | 12 + ...e-publisher-summary.checks-expected-result | 23 + ...t-duplicate-methods-statement-mcdc-fcc.xml | 786 ++++++++++++++++++ .../steps/vectorcast-statement-branch.xml | 215 +++++ .../steps/vectorcast-statement-mcdc-fcc.xml | 785 +++++++++++++++++ .../steps/vectorcast-statement-mcdc.xml | 141 ++++ .../publisher/threshold/GlobalThreshold.java | 1 - 35 files changed, 3209 insertions(+), 132 deletions(-) create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/VectorCastSourcePrinter.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageRecorderITest.java create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate-overview.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-details.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-overview.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-details.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-overview.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-details.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-overview.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-summary.checks-expected-result create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-duplicate-methods-statement-mcdc-fcc.xml create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-branch.xml create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc-fcc.xml create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc.xml diff --git a/README.md b/README.md index 0f8dc9ce..54084721 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The Jenkins Coverage Plug-in collects reports of code coverage or mutation cover - [JaCoCo](https://www.jacoco.org/jacoco): Code Coverage - [Cobertura](https://cobertura.github.io/cobertura/): Code Coverage - [OpenCover](https://github.com/OpenCover/opencover): Code Coverage +- [VectorCAST](https://www.vector.com/int/en/products/products-a-z/software/vectorcast): Code Coverage including MC/DC, Function, Function Call coverages - [PIT](https://pitest.org/): Mutation Coverage - [JUnit](https://ant.apache.org/manual/Tasks/junitreport.html): Test Results - [NUnit](https://nunit.org/): Test Results @@ -107,6 +108,7 @@ The Coverage Plug-in supports the following report formats: - [JaCoCo](https://www.jacoco.org/jacoco): Code Coverage - [Cobertura](https://cobertura.github.io/cobertura/): Code Coverage - [OpenCover](https://github.com/OpenCover/opencover): Code Coverage +- [VectorCAST](https://www.vector.com/int/en/products/products-a-z/software/vectorcast) Code Coverage including MC/DC, Function, Function Call coverages - [PIT](https://pitest.org/): Mutation Coverage - [JUnit](https://ant.apache.org/manual/Tasks/junitreport.html): Test Results - [NUnit](https://nunit.org/): Test Results diff --git a/plugin/pom.xml b/plugin/pom.xml index ffe68dfc..ccb587fd 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -25,7 +25,7 @@ jenkinsci/coverage-plugin - 0.45.0 + 0.46.0 1.17.2 diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageSeriesBuilder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageSeriesBuilder.java index 3508ab14..1e2b1ac7 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageSeriesBuilder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageSeriesBuilder.java @@ -20,6 +20,9 @@ public class CoverageSeriesBuilder extends SeriesBuilder { static final String BRANCH_COVERAGE = "branch"; static final String MUTATION_COVERAGE = "mutation"; static final String TEST_STRENGTH = "test-strength"; + static final String MCDC_PAIR_COVERAGE = "mcdc-pair"; + static final String FUNCTION_CALL_COVERAGE = "function-call"; + static final String METHOD_COVERAGE = "method"; @Override protected Map computeSeries(final CoverageStatistics statistics) { @@ -29,6 +32,10 @@ protected Map computeSeries(final CoverageStatistics statistics) add(statistics, Metric.BRANCH, BRANCH_COVERAGE, series); add(statistics, Metric.MUTATION, MUTATION_COVERAGE, series); add(statistics, Metric.TEST_STRENGTH, TEST_STRENGTH, series); + add(statistics, Metric.MCDC_PAIR, MCDC_PAIR_COVERAGE, series); + add(statistics, Metric.FUNCTION_CALL, FUNCTION_CALL_COVERAGE, series); + add(statistics, Metric.METHOD, METHOD_COVERAGE, series); + return series; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageTrendChart.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageTrendChart.java index 7e3da39f..ab9ab534 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageTrendChart.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/charts/CoverageTrendChart.java @@ -23,6 +23,26 @@ * @see JacksonFacade */ public class CoverageTrendChart { + /* Line Mode used to indicate whether is should be a filled line chart or line chart */ + private static FilledMode lineMode; + + /** + * Sets the line mode for the trend chart. + * + * @param dataSet + * + */ + private void setLineMode(final LinesDataSet dataSet) { + // If the dataset contains MCDC or Function Call Coverage + if (dataSet.containsSeries(CoverageSeriesBuilder.MCDC_PAIR_COVERAGE) + || dataSet.containsSeries(CoverageSeriesBuilder.FUNCTION_CALL_COVERAGE)) { + lineMode = FilledMode.LINES; + } + else { + lineMode = FilledMode.FILLED; + } + } + /** * Creates the chart for the specified results. * @@ -38,11 +58,13 @@ public LinesChartModel create(final Iterable> re final ChartModelConfiguration configuration) { CoverageSeriesBuilder builder = new CoverageSeriesBuilder(); LinesDataSet dataSet = builder.createDataSet(configuration, results); + + setLineMode(dataSet); LinesChartModel model = new LinesChartModel(dataSet); if (dataSet.isNotEmpty()) { LineSeries lineSeries = new LineSeries(Messages.Metric_LINE(), - JenkinsPalette.GREEN.normal(), StackedMode.SEPARATE_LINES, FilledMode.FILLED, + JenkinsPalette.GREEN.normal(), StackedMode.SEPARATE_LINES, lineMode, dataSet.getSeries(CoverageSeriesBuilder.LINE_COVERAGE)); model.addSeries(lineSeries); model.useContinuousRangeAxis(); @@ -55,6 +77,13 @@ public LinesChartModel create(final Iterable> re JenkinsPalette.GREEN.dark()); addSeries(dataSet, model, Messages.Metric_TEST_STRENGTH(), CoverageSeriesBuilder.TEST_STRENGTH, JenkinsPalette.GREEN.light()); + + addSeries(dataSet, model, Messages.Metric_MCDC_PAIR(), CoverageSeriesBuilder.MCDC_PAIR_COVERAGE, + JenkinsPalette.RED.light()); + addSeries(dataSet, model, Messages.Metric_METHOD(), CoverageSeriesBuilder.METHOD_COVERAGE, + JenkinsPalette.RED.normal()); + addSeries(dataSet, model, Messages.Metric_FUNCTION_CALL(), CoverageSeriesBuilder.FUNCTION_CALL_COVERAGE, + JenkinsPalette.RED.dark()); } return model; } @@ -63,7 +92,7 @@ private static void addSeries(final LinesDataSet dataSet, final LinesChartModel final String name, final String seriesId, final String color) { if (dataSet.containsSeries(seriesId)) { LineSeries branchSeries = new LineSeries(name, - color, StackedMode.SEPARATE_LINES, FilledMode.FILLED, + color, StackedMode.SEPARATE_LINES, lineMode, dataSet.getSeries(seriesId)); model.addSeries(branchSeries); 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 2ef45235..dceb0923 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 @@ -399,6 +399,10 @@ public String getDisplayName(final Metric metric) { return Messages.Metric_LOC(); case TESTS: return Messages.Metric_TESTS(); + case MCDC_PAIR: + return Messages.Metric_MCDC_PAIR(); + case FUNCTION_CALL: + return Messages.Metric_FUNCTION_CALL(); default: throw new NoSuchElementException("No display name found for metric " + metric); } @@ -472,6 +476,10 @@ public String getLabel(final Metric metric) { return Messages.Metric_Short_LOC(); case TESTS: return Messages.Metric_Short_TESTS(); + case MCDC_PAIR: + return Messages.Metric_Short_MCDC_PAIR(); + case FUNCTION_CALL: + return Messages.Metric_Short_FUNCTION_CALL(); default: throw new NoSuchElementException("No label found for metric " + metric); } @@ -527,6 +535,8 @@ public ListBoxModel getMetricItems() { add(options, Metric.COMPLEXITY_MAXIMUM); add(options, Metric.LOC); add(options, Metric.TESTS); + add(options, Metric.MCDC_PAIR); + add(options, Metric.FUNCTION_CALL); return options; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/CoverageSourcePrinter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/CoverageSourcePrinter.java index b7362ce4..15609339 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/CoverageSourcePrinter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/CoverageSourcePrinter.java @@ -17,13 +17,13 @@ */ class CoverageSourcePrinter implements Serializable { private static final long serialVersionUID = -6044649044983631852L; - private static final Sanitizer SANITIZER = new Sanitizer(); + protected static final Sanitizer SANITIZER = new Sanitizer(); static final String UNDEFINED = "noCover"; static final String NO_COVERAGE = "coverNone"; static final String FULL_COVERAGE = "coverFull"; static final String PARTIAL_COVERAGE = "coverPart"; - private static final String NBSP = " "; + protected static final String NBSP = " "; private final String path; private final int[] linesToPaint; @@ -54,7 +54,7 @@ public String renderLine(final int line, final String sourceCode) { .render(); } - private String cleanupCode(final String content) { + protected String cleanupCode(final String content) { return content.replace("\n", StringUtils.EMPTY) .replace("\r", StringUtils.EMPTY) .replace(" ", NBSP) @@ -133,4 +133,8 @@ int getCounter(final int line, final int... counters) { } return 0; } -} + + public String getColumnHeader() { + return StringUtils.EMPTY; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/SourceCodePainter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/SourceCodePainter.java index 6ea8a556..c73bbfc3 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/SourceCodePainter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/SourceCodePainter.java @@ -91,6 +91,10 @@ public void processSourceCodePainting(final Node rootNode, final List private CoverageSourcePrinter createFileModel(final Node rootNode, final FileNode fileNode) { if (rootNode.getValue(Metric.MUTATION).isPresent()) { return new MutationSourcePrinter(fileNode); + } + else if (rootNode.getValue(Metric.MCDC_PAIR).isPresent() + || rootNode.getValue(Metric.FUNCTION_CALL).isPresent()) { + return new VectorCastSourcePrinter(fileNode); } else { return new CoverageSourcePrinter(fileNode); @@ -206,6 +210,9 @@ private int paint(final CoverageSourcePrinter paint, final String relativePathId Path fullSourcePath = paintedFilesFolder.resolve(sanitizedFileName); try (BufferedWriter output = Files.newBufferedWriter(fullSourcePath)) { List lines = Files.readAllLines(Paths.get(resolvedPath.getRemote()), charset); + + // added a header to display what is being shown in each column + output.write(paint.getColumnHeader()); for (int line = 0; line < lines.size(); line++) { output.write(paint.renderLine(line + 1, lines.get(line))); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/VectorCastSourcePrinter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/VectorCastSourcePrinter.java new file mode 100644 index 00000000..08d49dbd --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/source/VectorCastSourcePrinter.java @@ -0,0 +1,470 @@ +package io.jenkins.plugins.coverage.metrics.source; + +import org.apache.commons.lang3.StringUtils; + +import edu.hm.hafner.coverage.FileNode; + +import static j2html.TagCreator.*; + +/** + * Provides all required information for a {@link FileNode} so that its source code can be rendered together with the + * line and mutation coverage in HTML. + */ +@SuppressWarnings("PMD.GodClass") +public class VectorCastSourcePrinter extends CoverageSourcePrinter { + private static final long serialVersionUID = 7204367145168517936L; + + private final int[] mcdcPairCoveredPerLine; + private final int[] mcdcPairMissedPerLine; + + private final int[] functionCallCoveredPerLine; + private final int[] functionCallMissedPerLine; + + VectorCastSourcePrinter(final FileNode file) { + super(file); + mcdcPairCoveredPerLine = file.getMcdcPairCoveredCounters(); + mcdcPairMissedPerLine = file.getMcdcPairMissedCounters(); + + functionCallCoveredPerLine = file.getFunctionCallCoveredCounters(); + functionCallMissedPerLine = file.getFunctionCallMissedCounters(); + } + + /** + * Gets the tr HTML tag for this source line. Used for case where both MCDC and FCC are present. + * + * @param line + * line number for the summary data + * + * @param sourceCode + * line of source code + * + * @param isPainted + * indicator of if the line should be painted + * + * @param third + * third column string. + * + * @param fouth + * fouth column string. + * + * @return string for the html row + * + */ + private String getTr(final int line, final String sourceCode, final boolean isPainted, final String third, final String fouth) { + var trData = tr() + .withClass(isPainted ? getColorClass(line) : UNDEFINED) + .condAttr(isPainted, "data-html-tooltip", isPainted ? getTooltip(line) : StringUtils.EMPTY); + + trData.with( + td().withClass("line").with(a().withName(String.valueOf(line)).withText(String.valueOf(line))), + td().withClass("hits").with(isPainted ? text(getSummaryColumn(line)) : text(StringUtils.EMPTY)) + ); + + if (!third.equals(StringUtils.EMPTY)) { + trData.with(td().withClass("hits").with(isPainted ? text(third) : text(StringUtils.EMPTY))); + } + if (!fouth.equals(StringUtils.EMPTY)) { + trData.with(td().withClass("hits").with(isPainted ? text(fouth) : text(StringUtils.EMPTY))); + } + + trData.with(td().withClass("code").with(rawHtml(SANITIZER.render(cleanupCode(sourceCode))))); + + return trData.render(); + } + + /** + * Gets the tr HTML tag for this source line. Used for case where neither MCDC or FCC are present. + * + * @param line + * line number for the summary data + * + * @param sourceCode + * line of source code + * + * @param isPainted + * indicator of if the line should be painted + * + * @return string for the html row + * + */ + private String getTr(final int line, final String sourceCode, final boolean isPainted) { + return getTr(line, sourceCode, isPainted, StringUtils.EMPTY, StringUtils.EMPTY); + } + + /** + * Gets the tr HTML tag for this source line. Used for MCDC or FCC but not both. + * + * @param line + * line number for the summary data + * + * @param sourceCode + * line of source code + * + * @param isPainted + * indicator of if the line should be painted + * + * @param third + * third column string. + * + * @return string for the html row + * + */ + private String getTr(final int line, final String sourceCode, final boolean isPainted, final String third) { + return getTr(line, sourceCode, isPainted, third, StringUtils.EMPTY); + } + + /** + * Main call to render the source line in HTML table format. + * + * @param line + * line number for the summary data + * + * @param sourceCode + * line of source code + * + * @return string of the source code line in HTML format + * + */ + @Override + public String renderLine(final int line, final String sourceCode) { + var isPainted = isPainted(line); + var hasMcdc = hasAnyMcdcPairCoverage(); + var hasFc = hasAnyFunctionCallCoverage(); + + String trString; + + // If this file only has Line, St/Br, and FunctionCall + if (!hasMcdc && hasFc) { + trString = getTr(line, sourceCode, isPainted, getFunctionCallSummaryColumn(line)); + } + // If this file only has Line, St/Br, and MCDC + else if (hasMcdc && !hasFc) { + trString = getTr(line, sourceCode, isPainted, getMcdcPairSummaryColumn(line)); + } + // If this file only has Line, St/Br, FunctionCall and MCDC + else if (hasMcdc && hasFc) { + trString = getTr(line, sourceCode, isPainted, getFunctionCallSummaryColumn(line), getMcdcPairSummaryColumn(line)); + } + // If this file only has Line and St/Br + else { + trString = getTr(line, sourceCode, isPainted); + } + return trString; + } + + /** + * Gets the column header given MCDC or FCC but not both. + * + * @param third + * third column string. + * + * @return string for the column header + * + */ + private String getColumnHeader(final String third) { + return getColumnHeader(third, NBSP); + } + + /** + * Gets the column header. + * + * @param third + * third column string. + * + * @param fourth + * fourth column string. + * + * @return string for the column header + * + */ + private String getColumnHeader(final String third, final String fourth) { + return tr().withClass(UNDEFINED).with( + td().withClass("line").with(text("Line")), + td().withClass("line").with(text("St/Br")), + td().withClass("line").with(text(third)), + td().withClass("line").with(text(fourth)), + td().withClass("line").with(text(NBSP)) + ).render(); + } + + /** + * Gets the source code column header. + * + * @return string of the column header + * + */ + @Override + public String getColumnHeader() { + var hasMcdc = hasAnyMcdcPairCoverage(); + var hasFc = hasAnyFunctionCallCoverage(); + String trString; + + // If this file only has Line, St/Br, and FunctionCall + if (!hasMcdc && hasFc) { + trString = getColumnHeader("FCall"); + } + // If this file only has Line, St/Br, and MCDC + else if (hasMcdc && !hasFc) { + trString = getColumnHeader("MC/DC"); + } + // If this file only has Line, St/Br, FunctionCall and MCDC + else if (hasMcdc && hasFc) { + trString = getColumnHeader("FCall", "MC/DC"); + } + // If this file only has Line and St/Br + else { + // this is the original metrics so maybe don't print header? + trString = tr().withClass(UNDEFINED).with( + td().withClass("line").with(text("Line ")), + td().withClass("line").with(text("St/Br")), + td().withClass("line").with(text(NBSP)) + ).render(); + } + + return trString; + } + + /** + * Gets the color class depending on coverage types. + * + * @param line + * line number for the summary data + * + * @return string of the color + * + */ + @Override + public String getColorClass(final int line) { + if (getCovered(line) == 0 && getMcdcPairCovered(line) == 0 && getFunctionCallCovered(line) == 0) { + return NO_COVERAGE; + } + else if (getMissed(line) == 0 && getMcdcPairMissed(line) == 0 && getFunctionCallMissed(line) == 0) { + return FULL_COVERAGE; + } + else { + return PARTIAL_COVERAGE; + } + } + + /** + * Constructs the tooltip depending on the coverage. + * + * @param line + * line number for the summary data + * + * @return the MCDC Pair tooltip + * + */ + public String getMcdcPairTooltip(final int line) { + var mcdcPairCovered = getMcdcPairCovered(line); + var mcdcPairMissed = getMcdcPairMissed(line); + + return getTooltip(mcdcPairCovered, mcdcPairMissed, false, "MC/DC pairs"); + } + + /** + * Constructs the tooltip depending on the coverage. + * + * @param line + * line number for the summary data + * + * @return the function call tooltip + * + */ + + public String getfunctionCallTooltip(final int line) { + var functionCallCovered = getFunctionCallCovered(line); + var functionCallMissed = getFunctionCallMissed(line); + + return getTooltip(functionCallCovered, functionCallMissed, true, "Function calls"); + } + + /** + * Constructs the tooltip depending on the coverage. + * + * @param covered + * count of the covered coverage + * + * @param missed + * count of the missed coverage + * + * @param checkAny + * boolean to indicate if code should check for any covered bits + * + * @param description + * Description of the coverage - MCDC/FCC + * + * @return + * string of the tooltip + */ + public String getTooltip(final int covered, final int missed, final boolean checkAny, final String description) { + String tooltip = ""; + + if (covered + missed > 1) { + if (missed == 0) { + tooltip = String.format("All %s covered: %d/%d", description, covered, covered + missed); + } + else { + tooltip = String.format("%s partially covered: %d/%d", description, covered, covered + missed); + } + } + else if (checkAny && covered == 1) { + tooltip = String.format("%s covered", description); + } + + return tooltip; + } + + /** + * Constructs the tooltip depending on the coverage. + * + * @param line + * line number for the summary data + * + * @return + * string of the tooltip + */ + @Override + public String getTooltip(final int line) { + String toolTipString = ""; + + String lineBranchToolTipString = super.getTooltip(line); + String mcdcPairToolTipString = getMcdcPairTooltip(line); + String functionCallToolTipString = getfunctionCallTooltip(line); + + if (lineBranchToolTipString.length() > 0) { + toolTipString += lineBranchToolTipString; + } + if (mcdcPairToolTipString.length() > 0) { + if (toolTipString.length() > 0) { + toolTipString += " | "; + } + toolTipString += mcdcPairToolTipString; + } + if (functionCallToolTipString.length() > 0) { + if (toolTipString.length() > 0) { + toolTipString += " | "; + } + toolTipString += functionCallToolTipString; + } + + return toolTipString; + } + + /** + * Returns true if there is any MCDC Pair coverage. + * + * @return + * true if there is any MCDC Pair coverage + */ + public boolean hasAnyMcdcPairCoverage() { + boolean hasMcDc = false; + for (int i = 0; i < mcdcPairCoveredPerLine.length && !hasMcDc; i++) { + if ((mcdcPairCoveredPerLine[i] + mcdcPairMissedPerLine[i]) > 0) { + hasMcDc = true; + } + } + + return hasMcDc; + } + + /** + * Returns true if there is any Function Call coverage. + * + * @return + * true if there is any Function Call coverage + */ + + public boolean hasAnyFunctionCallCoverage() { + boolean hasFc = false; + for (int i = 0; i < functionCallMissedPerLine.length && !hasFc; i++) { + if ((functionCallCoveredPerLine[i] + functionCallMissedPerLine[i]) > 0) { + hasFc = true; + } + } + + return hasFc; + } + + /** + * Returns the count of covered MCDC Pairs on a line. + * + * @param line + * line number for the summary data + * @return + * the number of covered MCDC Pairs on a line + */ + public int getMcdcPairCovered(final int line) { + return getCounter(line, mcdcPairCoveredPerLine); + } + + /** + * Returns the count of missed MCDC Pairs on a line. + * + * @param line + * line number for the summary data + * @return + * the number of missed MCDC Pairs on a line + */ + public int getMcdcPairMissed(final int line) { + return getCounter(line, mcdcPairMissedPerLine); + } + + /** + * Returns the count of covered Function Calls on a line. + * + * @param line + * line number for the summary data + * @return + * the number of covered Function Calls on a line + */ + public int getFunctionCallCovered(final int line) { + return getCounter(line, functionCallCoveredPerLine); + } + + /** + * Returns the count of missed Function Calls on a line. + * + * @param line + * line number for the summary data + * @return + * the number of missed Function Calls on a line + */ + public int getFunctionCallMissed(final int line) { + return getCounter(line, functionCallMissedPerLine); + } + + /** + * Creates the MCDC Pairs summary column. + * + * @param line + * line number for the summary data + * @return + * string of the MCDC Pairs summary column + */ + public String getMcdcPairSummaryColumn(final int line) { + var covered = getMcdcPairCovered(line); + var missed = getMcdcPairMissed(line); + if (covered + missed > 1) { + return String.format("%d/%d", covered, covered + missed); + } + return String.valueOf(covered); + } + + /** + * Creates the function call summary column. + * + * @param line + * line number for the summary data + * @return + * string of the function call summary column + */ + public String getFunctionCallSummaryColumn(final int line) { + var covered = getFunctionCallCovered(line); + var missed = getFunctionCallMissed(line); + if (covered + missed > 1) { + return String.format("%d/%d", covered, covered + missed); + } + return String.valueOf(covered); + } +} 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 1f6cfe61..85075862 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 @@ -571,7 +571,8 @@ public double getTrend(final Baseline baseline, final Metric metric) { NavigableSet getMetricsForSummary() { return new TreeSet<>( Set.of(Metric.LINE, Metric.LOC, Metric.BRANCH, Metric.COMPLEXITY_DENSITY, - Metric.MUTATION, Metric.TEST_STRENGTH, Metric.TESTS)); + Metric.MUTATION, Metric.TEST_STRENGTH, Metric.TESTS, + Metric.MCDC_PAIR, Metric.FUNCTION_CALL)); } /** 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 d0b3f84d..1728cb0d 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 @@ -76,6 +76,14 @@ class CoverageChecksPublisher { this.annotationScope = annotationScope; } + private ChecksFormatter getFormatter() { + if (rootNode.getValue(Metric.FUNCTION_CALL).isPresent() + || rootNode.getValue(Metric.MCDC_PAIR).isPresent()) { + return new VectorCastFormatter(); + } + return new ChecksFormatter(); + } + /** * Publishes the coverage report as Checks to SCM platforms. * @@ -83,8 +91,7 @@ class CoverageChecksPublisher { * The task listener */ void publishCoverageReport(final TaskListener listener) { - var publisher = ChecksPublisherFactory.fromRun(action.getOwner(), listener); - publisher.publish(extractChecksDetails()); + ChecksPublisherFactory.fromRun(action.getOwner(), listener).publish(extractChecksDetails()); } @VisibleForTesting @@ -106,7 +113,7 @@ ChecksDetails extractChecksDetails() { } private String getChecksTitle() { - return getMetricsForTitle().stream() + return getFormatter().getTitleMetrics().stream() .map(this::format) .flatMap(Optional::stream) .collect(Collectors.joining(", ")); @@ -137,11 +144,6 @@ private String getDeltaDetails(final Baseline baseline, final Metric metric) { return StringUtils.EMPTY; } - private NavigableSet getMetricsForTitle() { - return new TreeSet<>( - Set.of(Metric.LINE, Metric.BRANCH, Metric.MUTATION)); - } - private String getSummary() { return getAnnotationSummary() + getOverallCoverageSummary() @@ -369,7 +371,7 @@ private String createDeltaBaselinesOverview() { description.append(getBulletListItem(1, formatText(TextFormat.BOLD, getUrlText(action.getTitle(baseline), getBaseUrl() + baseline.getUrl())))); - for (Value value : action.getValues(baseline)) { + for (Value value : getValues(baseline)) { String display = FORMATTER.formatDetailedValueWithMetric(value); if (action.hasDelta(baseline, value.getMetric())) { display += String.format(" - Delta: %s", action.formatDelta(baseline, value.getMetric())); @@ -382,11 +384,17 @@ private String createDeltaBaselinesOverview() { return description.toString(); } + private List getValues(final Baseline baseline) { + return action.getAllValues(baseline).stream() + .filter(value -> getFormatter().getOverviewMetrics().contains(value.getMetric())) + .collect(Collectors.toList()); + } + private String createProjectOverview() { StringBuilder description = new StringBuilder(getSectionHeader(TITLE_HEADER_LEVEL, "Project Overview")); description.append("No changes detected, that affect the code coverage.\n"); - for (Value value : action.getValues(Baseline.PROJECT)) { + for (Value value : getValues(Baseline.PROJECT)) { description.append(getBulletListItem(1, FORMATTER.formatDetailedValueWithMetric(value))); } @@ -421,6 +429,7 @@ 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())); @@ -458,7 +467,9 @@ private String getFormatDelta(final Baseline baseline, final Metric metric) { } private Stream getMetricStream() { - return Metric.getCoverageMetrics().stream().skip(1); + return Metric.getCoverageMetrics().stream() + .skip(1) + .filter(m -> rootNode.getValue(m).isPresent()); } private Collector asColumn() { @@ -530,4 +541,31 @@ private enum TextFormat { BOLD, CURSIVE } + + /** + * Determines the metrics that should be shown in the title, overview, and tables. + * Metrics without a valid value in the coverage tree will be skipped. + */ + private static class ChecksFormatter { + NavigableSet getTitleMetrics() { + return new TreeSet<>( + Set.of(Metric.LINE, Metric.BRANCH, Metric.MUTATION)); + } + + NavigableSet getOverviewMetrics() { + return new TreeSet<>( + Set.of(Metric.LINE, Metric.LOC, Metric.BRANCH, Metric.COMPLEXITY_DENSITY, + Metric.MUTATION, Metric.TEST_STRENGTH, Metric.TESTS, + Metric.MCDC_PAIR, Metric.FUNCTION_CALL)); + } + } + + private static class VectorCastFormatter extends ChecksFormatter { + @Override + NavigableSet getOverviewMetrics() { + var valueMetrics = super.getOverviewMetrics(); + valueMetrics.add(Metric.METHOD); + return valueMetrics; + } + } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTableModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTableModel.java index 2f4f4fe4..cb9f5e05 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTableModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTableModel.java @@ -35,6 +35,7 @@ import io.jenkins.plugins.datatables.TableConfiguration; import io.jenkins.plugins.datatables.TableConfiguration.SelectStyle; import io.jenkins.plugins.datatables.TableModel; +import org.apache.commons.lang3.StringUtils; import static j2html.TagCreator.*; @@ -118,6 +119,13 @@ public List getColumns() { Messages.Column_DeltaLineCoverage("Δ"), columns); configureValueColumn("branchCoverage", Metric.BRANCH, Messages.Column_BranchCoverage(), Messages.Column_DeltaBranchCoverage("Δ"), columns); + + /* VectorCAST metrics */ + configureValueColumn("mcdcPairCoverage", Metric.MCDC_PAIR, Messages.Column_MCDCPairs(), + "", columns); + configureValueColumn("functionCallCoverage", Metric.FUNCTION_CALL, Messages.Column_FunctionCall(), + "", columns); + configureValueColumn("mutationCoverage", Metric.MUTATION, Messages.Column_MutationCoverage(), Messages.Column_DeltaMutationCoverage("Δ"), columns); configureValueColumn("testStrength", Metric.TEST_STRENGTH, Messages.Column_TestStrength(), @@ -174,13 +182,15 @@ private void configureValueColumn(final String key, final Metric metric, final S .withResponsivePriority(1) .build(); columns.add(lineCoverage); - TableColumn lineCoverageDelta = new ColumnBuilder().withHeaderLabel(deltaHeaderLabel) - .withDataPropertyKey(key + "Delta") - .withDetailedCell() - .withType(ColumnType.NUMBER) - .withResponsivePriority(2) - .build(); - columns.add(lineCoverageDelta); + if (StringUtils.isNotEmpty(deltaHeaderLabel)) { + TableColumn lineCoverageDelta = new ColumnBuilder().withHeaderLabel(deltaHeaderLabel) + .withDataPropertyKey(key + "Delta") + .withDetailedCell() + .withType(ColumnType.NUMBER) + .withResponsivePriority(2) + .build(); + columns.add(lineCoverageDelta); + } } } @@ -251,6 +261,18 @@ public DetailedCell getBranchCoverage() { return createColoredCoverageColumn(getCoverageOfNode(Metric.BRANCH)); } + public DetailedCell getMethodCoverage() { + return createColoredCoverageColumn(getCoverageOfNode(Metric.METHOD)); + } + + public DetailedCell getMcdcPairCoverage() { + return createColoredCoverageColumn(getCoverageOfNode(Metric.MCDC_PAIR)); + } + + public DetailedCell getFunctionCallCoverage() { + return createColoredCoverageColumn(getCoverageOfNode(Metric.FUNCTION_CALL)); + } + public DetailedCell getMutationCoverage() { return createColoredCoverageColumn(getCoverageOfNode(Metric.MUTATION)); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTool.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTool.java index 90db7346..49fe9019 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTool.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/steps/CoverageTool.java @@ -214,6 +214,9 @@ public enum Parser { COBERTURA(Messages._Parser_Cobertura(), ParserType.COVERAGE, "**/cobertura.xml", "symbol-footsteps-outline plugin-ionicons-api"), + VECTORCAST(Messages._Parser_VectorCAST(), ParserType.COVERAGE, + "xml_data/cobertura/coverage_results*.xml", + "symbol-footsteps-outline plugin-ionicons-api"), JACOCO(Messages._Parser_JaCoCo(), ParserType.COVERAGE, "**/jacoco.xml", "symbol-footsteps-outline plugin-ionicons-api"), 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 d7e7eb3a..95f26f69 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 @@ -78,7 +78,8 @@ public class CoverageViewModel extends DefaultAsyncTableContentProvider implemen private static final ElementFormatter FORMATTER = new ElementFormatter(); private static final Set TREE_METRICS = Set.of( - Metric.LINE, Metric.BRANCH, Metric.MUTATION, Metric.TEST_STRENGTH, Metric.COMPLEXITY, Metric.TESTS); + Metric.LINE, Metric.BRANCH, Metric.MUTATION, Metric.TEST_STRENGTH, Metric.COMPLEXITY, Metric.TESTS, + Metric.MCDC_PAIR, Metric.FUNCTION_CALL); private static final String UNDEFINED = "-"; private final Run owner; private final String displayName; @@ -283,6 +284,15 @@ private Metric getCoverageMetricFromText(final String text) { if (text.contains("complexity")) { return Metric.COMPLEXITY; } + if (text.contains("mcdc-pair")) { + return Metric.MCDC_PAIR; + } + if (text.contains("function-call")) { + return Metric.FUNCTION_CALL; + } + if (text.contains("method")) { + return Metric.METHOD; + } throw new IllegalArgumentException("Unknown coverage metric: " + text); } diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/model/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/model/Messages.properties index 2c854201..3d6d9c2d 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/model/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/model/Messages.properties @@ -14,6 +14,8 @@ Metric.COMPLEXITY_DENSITY=Complexity Density Metric.COMPLEXITY_MAXIMUM=Maximum Cyclomatic Complexity Metric.LOC=Lines of Code Metric.TESTS=Test Cases +Metric.MCDC_PAIR=MC/DC Pair Coverage +Metric.FUNCTION_CALL=Function Call Coverage Metric.Short.CONTAINER=Container Metric.Short.MODULE=Module @@ -31,6 +33,8 @@ Metric.Short.COMPLEXITY_DENSITY=Complexity Density Metric.Short.COMPLEXITY_MAXIMUM=Max. Complexity Metric.Short.LOC=LOC Metric.Short.TESTS=Tests +Metric.Short.MCDC_PAIR=MC/DC Pair +Metric.Short.FUNCTION_CALL=Function Call Metric.MUTATION.Killed=Killed Metric.MUTATION.Survived=Survived diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageMetricColumn/help-metric.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageMetricColumn/help-metric.html index 1179cd75..d2230d7d 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageMetricColumn/help-metric.html +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageMetricColumn/help-metric.html @@ -22,6 +22,10 @@
Branch coverage or decision coverage (given as percentage)
MUTATION
Mutation coverage (given as percentage)
+
MCDC_PAIR
+
MC/DC Pair coverage (given as percentage)
+
FUNCTION_CALL
+
Function call coverage (given as percentage)
TEST_STRENGTH
Test Strength (given as percentage)
COMPLEXITY
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGate/help-metric.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGate/help-metric.html index 29ba28b1..e268c815 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGate/help-metric.html +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/steps/CoverageQualityGate/help-metric.html @@ -22,6 +22,10 @@
Branch coverage or decision coverage (given as percentage)
MUTATION
Mutation coverage (given as percentage)
+
MCDC_PAIR
+
MC/DC Pair coverage (given as percentage)
+
FUNCTION_CALL
+
Function call coverage(given as percentage)
TEST_STRENGTH
Test Strength (given as percentage)
COMPLEXITY
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 3d4c32aa..1ceffbbd 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 @@ -7,6 +7,7 @@ Parser.PIT=PIT Mutation Testing Reports Parser.Junit=JUnit Test Results Parser.Nunit=NUnit Test Results Parser.Xunit=XUnit Test Results +Parser.VectorCAST=VectorCAST Coverage Results Coverage.Not.Available=n/a Coverage.Link.Name=Coverage Report @@ -35,6 +36,9 @@ Column.Tests=Tests Column.Complexity=Complexity Column.MaxComplexity=Max. Complexity Column.ComplexityDensity=Complexity / LOC +Column.MCDCPairs=MC/DC Pairs +Column.DeltaMCDCPairsCoverage=MCDCPairs {0} +Column.FunctionCall=Function Call MessagesViewModel.Title=Code Coverage diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageTest.java index 6f135811..a27d691c 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageTest.java @@ -17,6 +17,7 @@ import edu.hm.hafner.coverage.Node; import edu.hm.hafner.coverage.Value; import edu.hm.hafner.coverage.parser.JacocoParser; +import edu.hm.hafner.coverage.parser.VectorCastParser; import edu.hm.hafner.util.FilteredLog; import edu.hm.hafner.util.ResourceTest; import edu.hm.hafner.util.SecureXmlParserFactory.ParsingException; @@ -56,6 +57,10 @@ protected Node readJacocoResult(final String fileName) { return readResult(fileName, new JacocoParser()); } + protected Node readVectorCastResult(final String fileName) { + return readResult(fileName, new VectorCastParser(CoverageParser.ProcessingMode.FAIL_FAST)); + } + /** * Reads and parses a JaCoCo coverage report. * 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 159b8743..9839a4e3 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 @@ -33,11 +33,11 @@ @DefaultLocale("en") class CoverageChecksPublisherTest extends AbstractCoverageTest { - private static final String JENKINS_BASE_URL = "http://127.0.0.1:8080"; - private static final String BUILD_LINK = "job/pipeline-coding-style/job/5"; - private static final String COVERAGE_ID = "coverage"; - private static final String REPORT_NAME = "Name"; - private static final int ANNOTATIONS_COUNT_FOR_MODIFIED = 3; + protected static final String JENKINS_BASE_URL = "http://127.0.0.1:8080"; + protected static final String BUILD_LINK = "job/pipeline-coding-style/job/5"; + protected static final String COVERAGE_ID = "coverage"; + protected static final String REPORT_NAME = "Name"; + protected static final int ANNOTATIONS_COUNT_FOR_MODIFIED = 3; @Test void shouldShowQualityGateDetails() { @@ -117,7 +117,7 @@ private void assertMutationAnnotations(final ChecksOutput output, final int expe } } - private void assertThatTitleIs(final CoverageChecksPublisher publisher, final String expectedTitle) { + protected void assertThatTitleIs(final CoverageChecksPublisher publisher, final String expectedTitle) { var checkDetails = publisher.extractChecksDetails(); assertThat(checkDetails.getOutput()).isPresent().get().satisfies(output -> { assertThat(output.getTitle()).isPresent().contains(expectedTitle); @@ -141,6 +141,97 @@ void shouldCreateChecksReportJaCoCo(final ChecksAnnotationScope scope, final int assertThatDetailsAreCorrect(checkDetails, expectedAnnotations); } + @Test + void shouldShowVectorCastQualityGateDetails() { + var result = readVectorCastResult("vectorcast-statement-mcdc-fcc.xml"); + + var publisher = new CoverageChecksPublisher(createActionWithoutDelta(result, + CoverageQualityGateEvaluatorTest.createQualityGateResult()), result, REPORT_NAME, + ChecksAnnotationScope.SKIP, createJenkins()); + + var checkDetails = publisher.extractChecksDetails(); + + var expectedQualityGateSummary = toString("vectorcast-coverage-publisher-quality-gate.checks-expected-result"); + assertThat(checkDetails.getOutput()).isPresent().get().satisfies(output -> { + assertThat(output.getSummary()).isPresent() + .get() + .asString() + .containsIgnoringWhitespaces(expectedQualityGateSummary); + }); + + var expectedOverview = toString("vectorcast-coverage-publisher-quality-gate-overview.checks-expected-result"); + assertThat(checkDetails.getOutput()).isPresent().get().satisfies(output -> { + assertThat(output.getSummary()).isPresent() + .get() + .asString() + .containsIgnoringWhitespaces(expectedOverview); + }); + } + + @Test + void shouldShowProjectBaselineForVectorCast() { + var result = readVectorCastResult("vectorcast-statement-mcdc-fcc.xml"); + + var publisher = new CoverageChecksPublisher(createActionWithoutDelta(result), result, REPORT_NAME, + ChecksAnnotationScope.SKIP, createJenkins()); + + assertThatTitleIs(publisher, "Line Coverage: 79.93%, Branch Coverage: 66.18%"); + } + + @ParameterizedTest(name = "should create checks (scope = {0}, expected annotations = {1})") + @CsvSource({"SKIP, 0", "ALL_LINES, 6", "MODIFIED_LINES, 0"}) + void shouldCreateChecksReportStatementBranch(final ChecksAnnotationScope scope, final int expectedAnnotations) { + shouldCreateChecksReport(scope, expectedAnnotations, + "vectorcast-statement-branch.xml", + "vectorcast-coverage-publisher-s+b-details.checks-expected-result", + "vectorcast-coverage-publisher-s+b-overview.checks-expected-result"); + } + + @ParameterizedTest(name = "should create checks (scope = {0}, expected annotations = {1})") + @CsvSource({"SKIP, 0", "ALL_LINES, 8", "MODIFIED_LINES, 0"}) + void shouldCreateChecksReportStatementMcdc(final ChecksAnnotationScope scope, final int expectedAnnotations) { + shouldCreateChecksReport(scope, expectedAnnotations, + "vectorcast-statement-mcdc.xml", + "vectorcast-coverage-publisher-s+mcdc-details.checks-expected-result", + "vectorcast-coverage-publisher-s+mcdc-overview.checks-expected-result"); + } + + @ParameterizedTest(name = "should create checks (scope = {0}, expected annotations = {1})") + @CsvSource({"SKIP, 0", "ALL_LINES, 59", "MODIFIED_LINES, 0"}) + void shouldCreateChecksReportStatementMcdcFunctionCall(final ChecksAnnotationScope scope, final int expectedAnnotations) { + shouldCreateChecksReport(scope, expectedAnnotations, + "vectorcast-statement-mcdc-fcc.xml", + "vectorcast-coverage-publisher-s+mcdc+fcc-details.checks-expected-result", + "vectorcast-coverage-publisher-s+mcdc+fcc-overview.checks-expected-result"); + } + + void shouldCreateChecksReport(final ChecksAnnotationScope scope, final int expectedAnnotations, + final String inFile, final String checkDetailsFile, final String checkOverviewFile) { + var result = readVectorCastResult(inFile); + + var publisher = new CoverageChecksPublisher(createCoverageBuildAction(result), result, REPORT_NAME, scope, createJenkins()); + + var checkDetails = publisher.extractChecksDetails(); + + assertThat(checkDetails.getName()).isPresent().contains(REPORT_NAME); + assertThat(checkDetails.getStatus()).isEqualTo(ChecksStatus.COMPLETED); + assertThat(checkDetails.getConclusion()).isEqualTo(ChecksConclusion.SUCCESS); + assertThat(checkDetails.getDetailsURL()).isPresent() + .contains("http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage"); + assertThatDetailsAreCorrect(checkDetails, expectedAnnotations, checkDetailsFile, checkOverviewFile); + } + + private void assertThatDetailsAreCorrect(final ChecksDetails checkDetails, final int expectedAnnotations, + final String checkDetailsFile, final String checkOverviewFile) { + assertThat(checkDetails.getOutput()).isPresent().get().satisfies(output -> { + assertThat(output.getTitle()).isPresent().contains("Line Coverage: 50.00% (+50.00%)"); + var expectedDetails = toString(checkDetailsFile); + assertThat(output.getText()).isPresent().get().asString().isEqualToNormalizingWhitespace(expectedDetails); + assertChecksAnnotations(output, expectedAnnotations); + assertSummary(output, checkOverviewFile); + }); + } + private void assertThatDetailsAreCorrect(final ChecksDetails checkDetails, final int expectedAnnotations) { assertThat(checkDetails.getOutput()).isPresent().get().satisfies(output -> { assertThat(output.getTitle()).isPresent().contains("Line Coverage: 50.00% (+50.00%)"); @@ -151,13 +242,13 @@ private void assertThatDetailsAreCorrect(final ChecksDetails checkDetails, final }); } - private void assertSummary(final ChecksOutput checksOutput, final String fileName) { + protected void assertSummary(final ChecksOutput checksOutput, final String fileName) { assertThat(checksOutput.getSummary()).isPresent() .get() .asString().isEqualToNormalizingWhitespace(toString(fileName)); } - private void assertChecksAnnotations(final ChecksOutput checksOutput, final int expectedAnnotations) { + protected void assertChecksAnnotations(final ChecksOutput checksOutput, final int expectedAnnotations) { if (expectedAnnotations == ANNOTATIONS_COUNT_FOR_MODIFIED) { assertThat(checksOutput.getChecksAnnotations()).hasSize(expectedAnnotations).satisfiesExactly( annotation -> { @@ -182,14 +273,14 @@ private void assertChecksAnnotations(final ChecksOutput checksOutput, final int } } - private JenkinsFacade createJenkins() { + protected JenkinsFacade createJenkins() { JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); when(jenkinsFacade.getAbsoluteUrl(BUILD_LINK, COVERAGE_ID)).thenReturn( JENKINS_BASE_URL + "/" + BUILD_LINK + "/" + COVERAGE_ID); return jenkinsFacade; } - private CoverageBuildAction createCoverageBuildAction(final Node result) { + protected CoverageBuildAction createCoverageBuildAction(final Node result) { var testCoverage = new CoverageBuilder(Metric.LINE) .withCovered(1) .withMissed(1) @@ -220,7 +311,7 @@ private CoverageBuildAction createCoverageBuildAction(final Node result) { new TreeMap<>(Map.of(Metric.LINE, Fraction.ONE_HALF)), List.of(testCoverage), false); } - private CoverageBuildAction createActionWithoutDelta(final Node result) { + protected CoverageBuildAction createActionWithoutDelta(final Node result) { return createActionWithoutDelta(result, new QualityGateResult()); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoveragePluginITest.java index fbb9ce9a..185fb2fb 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoveragePluginITest.java @@ -33,13 +33,24 @@ import static org.assertj.core.api.Assertions.*; /** - * Integration test for different JaCoCo, Cobertura, and PIT files. + * Integration tests for different parsers. */ class CoveragePluginITest extends AbstractCoverageITest { private static final String COBERTURA_HIGHER_COVERAGE_FILE = "cobertura-higher-coverage.xml"; private static final int COBERTURA_COVERED_LINES = 8; private static final int COBERTURA_MISSED_LINES = 0; private static final String NO_FILES_FOUND_ERROR_MESSAGE = "[-ERROR-] No files found for pattern '**/*xml'. Configuration error?"; + private static final String VECTORCAST_HIGHER_COVERAGE_FILE = "vectorcast-statement-mcdc-fcc.xml"; + private static final int VECTORCAST_COVERED_LINES = 235; + private static final int VECTORCAST_MISSED_LINES = 59; + private static final int VECTORCAST_COVERED_BRANCH = 180; + private static final int VECTORCAST_MISSED_BRANCH = 92; + private static final int VECTORCAST_COVERED_MCDC_PAIR = 24; + private static final int VECTORCAST_MISSED_MCDC_PAIR = 35; + private static final int VECTORCAST_COVERED_FUNCTION_CALL = 62; + private static final int VECTORCAST_MISSED_FUNCTION_CALL = 17; + private static final int VECTORCAST_COVERED_METHOD = 21; + private static final int VECTORCAST_MISSED_METHOD = 9; @Test void shouldFailWithoutParserInFreestyleJob() { @@ -167,7 +178,8 @@ private void verifyJaCoCoAction(final CoverageBuildAction coverageResult) { .build()); var tableModel = coverageResult.getTarget().getTableModel(CoverageViewModel.ABSOLUTE_COVERAGE_TABLE_ID); assertThat(tableModel) - .extracting(TableModel::getColumns).asList() + .extracting(TableModel::getColumns) + .asInstanceOf(LIST) .extracting("headerLabel") .containsExactly("Hash", "Modified", @@ -486,7 +498,8 @@ private void verifyOnePitResult(final ParameterizedJob project) { var tableModel = coverageResult.getTarget().getTableModel(CoverageViewModel.ABSOLUTE_COVERAGE_TABLE_ID); assertThat(tableModel) - .extracting(TableModel::getColumns).asList() + .extracting(TableModel::getColumns) + .asInstanceOf(LIST) .extracting("headerLabel") .containsExactly("Hash", "Modified", @@ -566,4 +579,383 @@ void shouldRecordResultsWithDifferentId() { // TODO: verify that two different trend charts are returned! } + + @Test @Issue("785") + void shouldIgnoreErrors() { + WorkflowJob job = createPipeline(); + copyFileToWorkspace(job, "cobertura-duplicate-methods.xml", "cobertura.xml"); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage tools: [[parser: 'COBERTURA']]\n" + + " }\n", true)); + + Run failure = buildWithResult(job, Result.FAILURE); + + assertThat(getConsoleLog(failure)) + .contains("java.lang.IllegalArgumentException: There is already the same child [METHOD] Enumerate()"); + + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage tools: [[parser: 'COBERTURA']], ignoreParsingErrors: true\n" + + " }\n", true)); + + Run success = buildWithResult(job, Result.SUCCESS); + + assertThat(getConsoleLog(success)) + .doesNotContain("java.lang.IllegalArgumentException"); + } + + @Test + void shouldIgnoreEmptyListOfFiles() { + WorkflowJob job = createPipeline(); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage tools: [[parser: 'JACOCO']]\n" + + " }\n", true)); + + Run run = buildWithResult(job, Result.SUCCESS); + + assertThat(getConsoleLog(run)) + .contains("Using default pattern '**/jacoco.xml' since user defined pattern is not set", + "[-ERROR-] No files found for pattern '**/jacoco.xml'. Configuration error?") + .containsPattern("Searching for all files in '.*' that match the pattern '\\*\\*/jacoco.xml'") + .doesNotContain("Expanding pattern"); + } + + @Test + void shouldParseFileWithJaCoCo() { + WorkflowJob job = createPipeline(); + copyFilesToWorkspace(job, "jacoco.xml"); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage tools: [[parser: 'JACOCO']]\n" + + " }\n", true)); + + Run run = buildWithResult(job, Result.SUCCESS); + + assertThat(getConsoleLog(run)) + .contains("Using default pattern '**/jacoco.xml' since user defined pattern is not set", + "-> found 1 file", + "MODULE: 100.00% (1/1)", + "PACKAGE: 100.00% (1/1)", + "FILE: 70.00% (7/10)", + "CLASS: 83.33% (15/18)", + "METHOD: 95.10% (97/102)", + "INSTRUCTION: 93.33% (1260/1350)", + "LINE: 91.02% (294/323)", + "BRANCH: 93.97% (109/116)", + "COMPLEXITY: 160") + .containsPattern("Searching for all files in '.*' that match the pattern '\\*\\*/jacoco.xml'") + .containsPattern("Successfully parsed file .*/jacoco.xml") + .doesNotContain("Expanding pattern"); + } + + private void assertContentOfFirstVectorCastRow(final CoverageRow r) { + assertThatCell(r.getFileName()) + .contains("title=\"CurrentRelease/database/src/database.c\""); + assertThat(r.getPackageName()).isEqualTo("CurrentRelease.database.src"); + assertThat(r.getTests()).isEqualTo(0); + assertThat(r.getComplexity()).isEqualTo(5); + assertThat(r.getLoc()).isEqualTo(17); + assertThat(r.getMaxComplexity()).isEqualTo(2); + assertThatCell(r.getLineCoverage()) + .contains("title=\"Covered: 17 - Missed: 0\">100.00%"); + assertThatCell(r.getLineCoverageDelta()).contains("n/a"); + assertThatCell(r.getBranchCoverage()) + .contains("title=\"Covered: 9 - Missed: 2\">81.82%"); + assertThatCell(r.getBranchCoverageDelta()).contains("n/a"); + assertThatCell(r.getMethodCoverage()) + .contains("title=\"Covered: 3 - Missed: 0\">100.00%"); + assertThatCell(r.getMcdcPairCoverage()) + .contains("title=\"Covered: 1 - Missed: 1\">50.00%"); + assertThatCell(r.getFunctionCallCoverage()) + .contains("title=\"Covered: 4 - Missed: 0\">100.00%"); + assertThatCell(r.getDensity()).contains("0.29"); + } + + @Test + void shouldRecordOneVectorCastResultInFreestyleJob() { + FreeStyleProject project = createFreestyleJob(Parser.VECTORCAST, VECTORCAST_HIGHER_COVERAGE_FILE); + + verifyOneVectorCastResult(project); + } + + @Test + void shouldRecordOneVectorCastResultInPipeline() { + WorkflowJob job = createPipeline(Parser.VECTORCAST, VECTORCAST_HIGHER_COVERAGE_FILE); + + verifyOneVectorCastResult(job); + } + + @Test + void shouldRecordOneVectorCastResultInDeclarativePipeline() { + WorkflowJob job = createDeclarativePipeline(Parser.VECTORCAST, VECTORCAST_HIGHER_COVERAGE_FILE); + + verifyOneVectorCastResult(job); + } + + private void verifyOneVectorCastResult(final ParameterizedJob project) { + Run build = buildSuccessfully(project); + + verifyVectorCastAction(build.getAction(CoverageBuildAction.class)); + } + + private void verifyVectorCastAction(final CoverageBuildAction coverageResult) { + assertThat(coverageResult.getAllValues(Baseline.PROJECT)).extracting(Value::getMetric) + .containsExactly(Metric.MODULE, + Metric.PACKAGE, + Metric.FILE, + Metric.CLASS, + Metric.METHOD, + Metric.LINE, + Metric.BRANCH, + Metric.MCDC_PAIR, + Metric.FUNCTION_CALL, + Metric.COMPLEXITY, + Metric.COMPLEXITY_MAXIMUM, + Metric.COMPLEXITY_DENSITY, + Metric.LOC); + assertThat(coverageResult.getAllValues(Baseline.PROJECT)).contains( + new CoverageBuilder() + .withMetric(Metric.LINE) + .withCovered(VECTORCAST_COVERED_LINES) + .withMissed(VECTORCAST_MISSED_LINES) + .build()); + assertThat(coverageResult.getAllValues(Baseline.PROJECT)).contains( + new CoverageBuilder() + .withMetric(Metric.BRANCH) + .withCovered(VECTORCAST_COVERED_BRANCH) + .withMissed(VECTORCAST_MISSED_BRANCH) + .build()); + assertThat(coverageResult.getAllValues(Baseline.PROJECT)).contains( + new CoverageBuilder() + .withMetric(Metric.FUNCTION_CALL) + .withCovered(VECTORCAST_COVERED_FUNCTION_CALL) + .withMissed(VECTORCAST_MISSED_FUNCTION_CALL) + .build()); + assertThat(coverageResult.getAllValues(Baseline.PROJECT)).contains( + new CoverageBuilder() + .withMetric(Metric.MCDC_PAIR) + .withCovered(VECTORCAST_COVERED_MCDC_PAIR) + .withMissed(VECTORCAST_MISSED_MCDC_PAIR) + .build()); + assertThat(coverageResult.getAllValues(Baseline.PROJECT)).contains( + new CoverageBuilder() + .withMetric(Metric.METHOD) + .withCovered(VECTORCAST_COVERED_METHOD) + .withMissed(VECTORCAST_MISSED_METHOD) + .build()); + + var tableModel = coverageResult.getTarget().getTableModel(CoverageViewModel.ABSOLUTE_COVERAGE_TABLE_ID); + assertThat(tableModel) + .extracting(TableModel::getColumns) + .asInstanceOf(LIST) + .extracting("headerLabel") + .containsExactly("Hash", + "Modified", + "File", + "Package", + "Line", + "Line Δ", + "Branch", + "Branch Δ", + "MC/DC Pairs", + "Function Call", + "LOC", + "Complexity", + "Max. Complexity", + "Complexity / LOC"); + assertThat(coverageResult.getAllValues(Baseline.PROJECT)) + .contains(createLineCoverageBuilder() + .withCovered(VECTORCAST_COVERED_LINES) + .withMissed(VECTORCAST_MISSED_LINES) + .build()); + assertThat(tableModel.getRows()) + .hasSize(8) + .first() + .isInstanceOfSatisfying(CoverageRow.class, this::assertContentOfFirstVectorCastRow); + } + + @Test + void shouldRecordVectorCastAndJacocoResultsInFreestyleJob() { + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, + VECTORCAST_HIGHER_COVERAGE_FILE); + + CoverageRecorder recorder = new CoverageRecorder(); + + var vectorcast = new CoverageTool(); + vectorcast.setParser(Parser.VECTORCAST); + vectorcast.setPattern(VECTORCAST_HIGHER_COVERAGE_FILE); + + var jacoco = new CoverageTool(); + jacoco.setParser(Parser.JACOCO); + jacoco.setPattern(JACOCO_ANALYSIS_MODEL_FILE); + + recorder.setTools(List.of(jacoco, vectorcast)); + project.getPublishersList().add(recorder); + + verifyForOneVectorCastAndOneJacoco(project); + } + + @Test + void shouldRecordVectorCastAndJacocoResultsInPipeline() { + WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, VECTORCAST_HIGHER_COVERAGE_FILE); + + setPipelineScript(job, + "recordCoverage tools: [" + + "[parser: 'VECTORCAST', pattern: '" + VECTORCAST_HIGHER_COVERAGE_FILE + "']," + + "[parser: 'JACOCO', pattern: '" + JACOCO_ANALYSIS_MODEL_FILE + "']" + + "]"); + + verifyForOneVectorCastAndOneJacoco(job); + } + + @Test + void shouldRecordVectorCastAndJacocoResultsInDeclarativePipeline() { + WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, VECTORCAST_HIGHER_COVERAGE_FILE); + + job.setDefinition(new CpsFlowDefinition("pipeline {\n" + + " agent any\n" + + " stages {\n" + + " stage('Test') {\n" + + " steps {\n" + + " recordCoverage(tools: [\n" + + " [parser: 'VECTORCAST', pattern: '" + VECTORCAST_HIGHER_COVERAGE_FILE + "'],\n" + + " [parser: 'JACOCO', pattern: '" + JACOCO_ANALYSIS_MODEL_FILE + "']\n" + + " ])\n" + + " }\n" + + " }\n" + + " }\n" + + "}", true)); + + verifyForOneVectorCastAndOneJacoco(job); + } + + private void verifyForOneVectorCastAndOneJacoco(final ParameterizedJob project) { + Run build = buildSuccessfully(project); + + CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getAllValues(Baseline.PROJECT)) + .contains(createLineCoverageBuilder() + .withCovered(JACOCO_ANALYSIS_MODEL_COVERED + VECTORCAST_COVERED_LINES) + .withMissed(JACOCO_ANALYSIS_MODEL_MISSED + VECTORCAST_MISSED_LINES) + .build()); + + assertThat(getConsoleLog(build)).contains( + "[Coverage] Recording coverage results", + "[Coverage] Creating parser for VectorCAST Coverage Results", + "that match the pattern 'vectorcast-statement-mcdc-fcc.xml'", + "Successfully processed file 'vectorcast-statement-mcdc-fcc.xml'", + "[Coverage] Creating parser for JaCoCo Coverage Reports", + "that match the pattern 'jacoco-analysis-model.xml'", + "Successfully processed file 'jacoco-analysis-model.xml'"); + var log = coverageResult.getLog(); + assertThat(log.getInfoMessages()).contains("Recording coverage results", + "Creating parser for VectorCAST Coverage Results", + "Successfully processed file 'vectorcast-statement-mcdc-fcc.xml'", + "Creating parser for JaCoCo Coverage Reports", + "Successfully processed file 'jacoco-analysis-model.xml'", + "Resolving source code files...", + "-> finished resolving of absolute paths (found: 0, not found: 315)", + "Obtaining result action of reference build", + "Reference build recorder is not configured", + "-> Found no reference build", + "No quality gates have been set - skipping", + "Executing source code painting...", + "Painting 315 source files on agent", + "-> finished painting (0 files have been painted, 315 files failed)", + "Copying painted sources from agent to build folder", + "-> extracting...", + "-> done", + "Finished coverage processing - adding the action to the build..."); + assertThat(log.getErrorMessages()).contains( + "Errors while resolving source files on agent:", + "- Source file 'edu/hm/hafner/analysis/parser/PerlCriticParser.java' not found", + "- Source file 'edu/hm/hafner/analysis/parser/StyleCopParser.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/RoboCopyDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/parser/fxcop/FxCopRuleSet.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/SpotBugsDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/parser/AcuCobolParser.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/FlexSdkDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/BrakemanDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/PyDocStyleDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/parser/AjcParser.java' not found", + "- Source file 'edu/hm/hafner/analysis/ReaderFactory.java' not found", + "- Source file 'edu/hm/hafner/analysis/parser/DiabCParser.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/OELintAdvDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/GnuFortranDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/parser/Armcc5CompilerParser.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/DoxygenDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/ProtoLintDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/registry/GoLintDescriptor.java' not found", + "- Source file 'edu/hm/hafner/analysis/ModuleResolver.java' not found", + " ... skipped logging of 295 additional errors ..."); + } + + @Test + void shouldRecordVerctorCastResultsWithDifferentId() { + WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, VECTORCAST_HIGHER_COVERAGE_FILE); + + setPipelineScript(job, + "recordCoverage " + + "tools: [[parser: 'VECTORCAST', pattern: '" + VECTORCAST_HIGHER_COVERAGE_FILE + "']]," + + "id: 'vectorcast', name: 'VectorCast Results'\n" + + "recordCoverage " + + "tools: [" + + "[parser: 'JACOCO', pattern: '" + JACOCO_ANALYSIS_MODEL_FILE + "']]," + + "id: 'jacoco', name: 'JaCoCo Results'\n"); + + Run build = buildSuccessfully(job); + + List coverageResult = build.getActions(CoverageBuildAction.class); + assertThat(coverageResult).hasSize(2); + + assertThat(coverageResult).element(0).satisfies( + a -> { + assertThat(a.getUrlName()).isEqualTo("vectorcast"); + assertThat(a.getDisplayName()).isEqualTo("VectorCast Results"); + verifyVectorCastAction(a); + } + ); + assertThat(coverageResult).element(1).satisfies( + a -> { + assertThat(a.getUrlName()).isEqualTo("jacoco"); + assertThat(a.getDisplayName()).isEqualTo("JaCoCo Results"); + verifyJaCoCoAction(a); + }); + + // TODO: verify that two different trend charts are returned! + } + + @Test + void shouldParseFileWithVectorCast() { + WorkflowJob job = createPipeline(); + copyFileToWorkspace(job, "vectorcast-statement-mcdc-fcc.xml", "xml_data/cobertura/coverage_results_test.xml"); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage tools: [[parser: 'VECTORCAST']]\n" + + " }\n", true)); + + Run run = buildWithResult(job, Result.SUCCESS); + + assertThat(getConsoleLog(run)) + .contains("Using default pattern 'xml_data/cobertura/coverage_results*.xml' since user defined pattern is not set", + "-> found 1 file", + "MODULE: 100.00% (1/1)", + "PACKAGE: 100.00% (5/5)", + "FILE: 75.00% (6/8)", + "CLASS: 75.00% (6/8)", + "METHOD: 70.00% (21/30)", + "LINE: 79.93% (235/294)", + "BRANCH: 66.18% (180/272)", + "MCDC_PAIR: 40.68% (24/59)", + "FUNCTION_CALL: 78.48% (62/79)", + "COMPLEXITY: 100", + "COMPLEXITY_MAXIMUM: 26", + "COMPLEXITY_DENSITY: 100/294", + "LOC: 294") + .containsPattern("Searching for all files in '.*' that match the pattern 'xml_data/cobertura/coverage_results\\*.xml'") + .containsPattern("Successfully parsed file '.*/xml_data/cobertura/coverage_results_test.xml") + .doesNotContain("Expanding pattern"); + } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageRecorderITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageRecorderITest.java deleted file mode 100644 index 85f1e519..00000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/steps/CoverageRecorderITest.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.jenkins.plugins.coverage.metrics.steps; - -import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.Issue; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.Result; -import hudson.model.Run; - -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Tests the class {@link CoverageRecorder}. - * - * @author Ullrich Hafner - */ -class CoverageRecorderITest extends IntegrationTestWithJenkinsPerSuite { - @Test @Issue("785") - void shouldIgnoreErrors() { - WorkflowJob job = createPipeline(); - copyFileToWorkspace(job, "cobertura-duplicate-methods.xml", "cobertura.xml"); - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + " recordCoverage tools: [[parser: 'COBERTURA']]\n" - + " }\n", true)); - - Run failure = buildWithResult(job, Result.FAILURE); - - assertThat(getConsoleLog(failure)) - .contains("java.lang.IllegalArgumentException: There is already the same child [METHOD] Enumerate()"); - - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + " recordCoverage tools: [[parser: 'COBERTURA']], ignoreParsingErrors: true\n" - + " }\n", true)); - - Run success = buildWithResult(job, Result.SUCCESS); - - assertThat(getConsoleLog(success)) - .doesNotContain("java.lang.IllegalArgumentException"); - } - - @Test - void shouldIgnoreEmptyListOfFiles() { - WorkflowJob job = createPipeline(); - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + " recordCoverage tools: [[parser: 'JACOCO']]\n" - + " }\n", true)); - - Run run = buildWithResult(job, Result.SUCCESS); - - assertThat(getConsoleLog(run)) - .contains("Using default pattern '**/jacoco.xml' since user defined pattern is not set", - "[-ERROR-] No files found for pattern '**/jacoco.xml'. Configuration error?") - .containsPattern("Searching for all files in '.*' that match the pattern '\\*\\*/jacoco.xml'") - .doesNotContain("Expanding pattern"); - } - - @Test - void shouldParseFileWithJaCoCo() { - WorkflowJob job = createPipeline(); - copyFilesToWorkspace(job, "jacoco.xml"); - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + " recordCoverage tools: [[parser: 'JACOCO']]\n" - + " }\n", true)); - - Run run = buildWithResult(job, Result.SUCCESS); - - assertThat(getConsoleLog(run)) - .contains("Using default pattern '**/jacoco.xml' since user defined pattern is not set", - "-> found 1 file", - "MODULE: 100.00% (1/1)", - "PACKAGE: 100.00% (1/1)", - "FILE: 70.00% (7/10)", - "CLASS: 83.33% (15/18)", - "METHOD: 95.10% (97/102)", - "INSTRUCTION: 93.33% (1260/1350)", - "LINE: 91.02% (294/323)", - "BRANCH: 93.97% (109/116)", - "COMPLEXITY: 160") - .containsPattern("Searching for all files in '.*' that match the pattern '\\*\\*/jacoco.xml'") - .containsPattern("Successfully parsed file .*/jacoco.xml") - .doesNotContain("Expanding pattern"); - } -} diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate-overview.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate-overview.checks-expected-result new file mode 100644 index 00000000..1b33ab3c --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate-overview.checks-expected-result @@ -0,0 +1,26 @@ +#### Project Overview + +No changes detected, that affect the code coverage. +* Method Coverage: 70.00% (21/30) +* Line Coverage: 79.93% (235/294) +* Branch Coverage: 66.18% (180/272) +* MC/DC Pair Coverage: 40.68% (24/59) +* Function Call Coverage: 78.48% (62/79) +* Complexity Density: 0.34% +* Lines of Code: 294 + +#### Quality Gates Summary + +Overall result: Failed +- Overall project - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Overall project - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Modified code lines - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Modified code lines - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Modified files - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Modified files - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Overall project (difference to reference job) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Overall project (difference to reference job) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00) +- Modified code lines (difference to modified files) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Modified code lines (difference to modified files) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00) +- Modified files (difference to reference job) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Modified files (difference to reference job) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00) diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate.checks-expected-result new file mode 100644 index 00000000..a765f994 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-quality-gate.checks-expected-result @@ -0,0 +1,15 @@ +#### Quality Gates Summary + +Overall result: Failed +- Overall project - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Overall project - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Modified code lines - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Modified code lines - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Modified files - File Coverage: ≪Failed≫ - (Actual value: 75.00%, Quality gate: 76.00) +- Modified files - Line Coverage: ≪Failed≫ - (Actual value: 50.00%, Quality gate: 51.00) +- Overall project (difference to reference job) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Overall project (difference to reference job) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00) +- Modified code lines (difference to modified files) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Modified code lines (difference to modified files) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00) +- Modified files (difference to reference job) - File Coverage: ≪Failed≫ - (Actual value: -10.00%, Quality gate: 10.00) +- Modified files (difference to reference job) - Line Coverage: ≪Failed≫ - (Actual value: +5.00%, Quality gate: 10.00) diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-details.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-details.checks-expected-result new file mode 100644 index 00000000..bf61d508 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-details.checks-expected-result @@ -0,0 +1,11 @@ +#### Project coverage details + +||Module Coverage|Package Coverage|File Coverage|Class Coverage|Line Coverage|Branch Coverage +|:---:|:---:|:---:|:---:|:---:|:---:|:---: +:feet: **Overall project**|100.00% (1/1)|100.00% (5/5)|100.00% (3/3)|100.00% (3/3)|65.00% (52/80)|67.65% (23/34) +:chart_with_upwards_trend: **Overall project (difference to reference job)**|+0.00%|-50.00% :arrow_down:|n/a|n/a|+50.00% :arrow_up:|n/a +:feet: **Modified files**|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a +:chart_with_upwards_trend: **Modified files (difference to reference job)**|n/a|n/a|n/a|n/a|+50.00% :arrow_up:|n/a +:feet: **Modified code lines**|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a +:chart_with_upwards_trend: **Modified code lines (difference to modified files)**|n/a|n/a|n/a|n/a|+50.00% :arrow_up:|n/a +:feet: **Indirect changes**|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-overview.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-overview.checks-expected-result new file mode 100644 index 00000000..78d6ad2b --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+b-overview.checks-expected-result @@ -0,0 +1,11 @@ +#### Project Overview + +No changes detected, that affect the code coverage. +* Line Coverage: 65.00% (52/80) +* Branch Coverage: 67.65% (23/34) +* Complexity Density: 0.31% +* Lines of Code: 80 + +#### Quality Gates Summary + +No active quality gates. \ No newline at end of file diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-details.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-details.checks-expected-result new file mode 100644 index 00000000..bf9f6189 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-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|MC/DC Pair Coverage|Function Call Coverage +|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---: +:feet: **Overall project**|100.00% (1/1)|100.00% (10/10)|75.00% (6/8)|75.00% (6/8)|70.00% (21/30)|79.93% (235/294)|66.18% (180/272)|40.68% (24/59)|78.48% (62/79) +: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|n/a +:feet: **Modified files**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a|n/a +:chart_with_upwards_trend: **Modified files (difference to reference job)**|n/a|n/a|n/a|n/a|n/a|+50.00% :arrow_up:|n/a|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|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|n/a +:feet: **Indirect changes**|n/a|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a|n/a diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-overview.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-overview.checks-expected-result new file mode 100644 index 00000000..cb1e02f5 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc+fcc-overview.checks-expected-result @@ -0,0 +1,14 @@ +#### Project Overview + +No changes detected, that affect the code coverage. +* Method Coverage: 70.00% (21/30) +* Line Coverage: 79.93% (235/294) +* Branch Coverage: 66.18% (180/272) +* MC/DC Pair Coverage: 40.68% (24/59) +* Function Call Coverage: 78.48% (62/79) +* Complexity Density: 0.34% +* Lines of Code: 294 + +#### Quality Gates Summary + +No active quality gates. diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-details.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-details.checks-expected-result new file mode 100644 index 00000000..5e130de0 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-details.checks-expected-result @@ -0,0 +1,11 @@ +#### Project coverage details + +||Module Coverage|Package Coverage|File Coverage|Class Coverage|Line Coverage|Branch Coverage|MC/DC Pair Coverage +|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---: +:feet: **Overall project**|100.00% (1/1)|80.00% (4/5)|66.67% (2/3)|66.67% (2/3)|65.22% (45/69)|71.43% (20/28)|66.67% (8/12) +:chart_with_upwards_trend: **Overall project (difference to reference job)**|+0.00%|-50.00% :arrow_down:|n/a|n/a|+50.00% :arrow_up:|n/a|n/a +:feet: **Modified files**|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a +:chart_with_upwards_trend: **Modified files (difference to reference job)**|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|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|+50.00% :arrow_up:|n/a|n/a +:feet: **Indirect changes**|n/a|n/a|n/a|n/a|50.00% (1/2)|n/a|n/a diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-overview.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-overview.checks-expected-result new file mode 100644 index 00000000..5538923d --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-s+mcdc-overview.checks-expected-result @@ -0,0 +1,12 @@ +#### Project Overview + +No changes detected, that affect the code coverage. +* Line Coverage: 65.22% (45/69) +* Branch Coverage: 71.43% (20/28) +* MC/DC Pair Coverage: 66.67% (8/12) +* Complexity Density: 0.36% +* Lines of Code: 69 + +#### Quality Gates Summary + +No active quality gates. diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-summary.checks-expected-result b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-summary.checks-expected-result new file mode 100644 index 00000000..14c58b12 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-coverage-publisher-summary.checks-expected-result @@ -0,0 +1,23 @@ +#### Summary for modified lines + +- 3 lines have been modified +- 2 lines are 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% + * Branch Coverage: 93.97% (109/116) + * Complexity Density: 0.50% + * Lines of Code: 323 +* **[Modified files (difference to reference job)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#modifiedFilesCoverage)** + * Line Coverage: 50.00% (1/2) - Delta: +50.00% +* **[Modified code lines (difference to modified files)](http://127.0.0.1:8080/job/pipeline-coding-style/job/5/coverage#modifiedLinesCoverage)** + * Line Coverage: 50.00% (1/2) - Delta: +50.00% +* **[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 + +No active quality gates. diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-duplicate-methods-statement-mcdc-fcc.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-duplicate-methods-statement-mcdc-fcc.xml new file mode 100644 index 00000000..aa22d856 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-duplicate-methods-statement-mcdc-fcc.xml @@ -0,0 +1,786 @@ + + + + + ./ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-branch.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-branch.xml new file mode 100644 index 00000000..4a589690 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-branch.xml @@ -0,0 +1,215 @@ + + + + ./ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc-fcc.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc-fcc.xml new file mode 100644 index 00000000..c1ab4334 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc-fcc.xml @@ -0,0 +1,785 @@ + + + + + ./ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc.xml new file mode 100644 index 00000000..725e4a65 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/steps/vectorcast-statement-mcdc.xml @@ -0,0 +1,141 @@ + + + + + ./ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-tests/src/main/java/io/jenkins/plugins/coverage/publisher/threshold/GlobalThreshold.java b/ui-tests/src/main/java/io/jenkins/plugins/coverage/publisher/threshold/GlobalThreshold.java index bdf6100b..7375c894 100644 --- a/ui-tests/src/main/java/io/jenkins/plugins/coverage/publisher/threshold/GlobalThreshold.java +++ b/ui-tests/src/main/java/io/jenkins/plugins/coverage/publisher/threshold/GlobalThreshold.java @@ -52,7 +52,6 @@ public enum GlobalThresholdTarget { FILE("File"), CLASS("Class"), METHOD("Method"), - FUNCTION("Function"), INSTRUCTION("Instruction"), LINE("Line"), CONDITIONAL("Conditional");