diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java index f788e8594..38bc8aff1 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java @@ -3,8 +3,11 @@ import java.io.Serializable; import java.util.Locale; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.Fraction; +import edu.hm.hafner.util.VisibleForTesting; + /** * Value of a code coverage item. The code coverage is measured using the number of covered and missed items. The type * of items (line, instruction, branch, file, etc.) is provided by the companion class {@link CoverageMetric}. @@ -17,6 +20,39 @@ public final class Coverage implements Serializable { /** Null object that indicates that the code coverage has not been measured. */ public static final Coverage NO_COVERAGE = new Coverage(0, 0); + /** + * Creates a new {@link Coverage} instance from the provided string representation. The string representation is + * expected to contain the number of covered items and the total number of items - separated by a slash, e.g. + * "100/345", or "0/0". Whitespace characters will be ignored. + * + * @param stringRepresentation + * string representation to convert from + * + * @return the created coverage + * @throws IllegalArgumentException + * if the string is not a valid Coverage instance + */ + public static Coverage valueOf(final String stringRepresentation) { + try { + String cleanedFormat = StringUtils.deleteWhitespace(stringRepresentation); + if (StringUtils.contains(cleanedFormat, "/")) { + String extractedCovered = StringUtils.substringBefore(cleanedFormat, "/"); + String extractedTotal = StringUtils.substringAfter(cleanedFormat, "/"); + + int covered = Integer.parseInt(extractedCovered); + int total = Integer.parseInt(extractedTotal); + if (total >= covered) { + return new CoverageBuilder().setCovered(covered).setMissed(total - covered).build(); + } + } + } + catch (NumberFormatException exception) { + // ignore and throw a specific exception + } + throw new IllegalArgumentException( + String.format("Cannot convert %s to a valid Coverage instance.", stringRepresentation)); + } + private final int covered; private final int missed; @@ -28,7 +64,7 @@ public final class Coverage implements Serializable { * @param missed * the number of missed items */ - public Coverage(final int covered, final int missed) { + private Coverage(final int covered, final int missed) { this.covered = covered; this.missed = missed; } @@ -80,8 +116,8 @@ public int getRoundedPercentage() { } /** - * Formats the covered percentage as String (with a precision of two digits after the comma). Uses {@code - * Locale.getDefault()} to format the percentage. + * Formats the covered percentage as String (with a precision of two digits after the comma). Uses + * {@code Locale.getDefault()} to format the percentage. * * @return the covered percentage * @see #formatCoveredPercentage(Locale) @@ -136,8 +172,8 @@ public CoveragePercentage getMissedPercentage() { } /** - * Formats the missed percentage as formatted String (with a precision of two digits after the comma). Uses {@code - * Locale.getDefault()} to format the percentage. + * Formats the missed percentage as formatted String (with a precision of two digits after the comma). Uses + * {@code Locale.getDefault()} to format the percentage. * * @return the missed percentage */ @@ -173,8 +209,9 @@ private String printPercentage(final Locale locale, final CoveragePercentage cov * @return the sum of this and the additional coverage */ public Coverage add(final Coverage additional) { - return new Coverage(covered + additional.getCovered(), - missed + additional.getMissed()); + return new CoverageBuilder().setCovered(covered + additional.getCovered()) + .setMissed(missed + additional.getMissed()) + .build(); } @Override @@ -217,4 +254,123 @@ public int hashCode() { result = 31 * result + missed; return result; } + + /** + * Returns a string representation for this {@link Coverage} that can be used to serialize this instance in a simple + * but still readable way. The serialization contains the number of covered items and the total number of items - + * separated by a slash, e.g. "100/345", or "0/0". + * + * @return a string representation for this {@link Coverage} + */ + public String serializeToString() { + return String.format("%d/%d", getCovered(), getTotal()); + } + + /** + * Builder to create an cache new {@link Coverage} instances. + */ + public static class CoverageBuilder { + @VisibleForTesting + static final int CACHE_SIZE = 16; + private static final Coverage[] CACHE = new Coverage[CACHE_SIZE * CACHE_SIZE]; + + static { + for (int covered = 0; covered < CACHE_SIZE; covered++) { + for (int missed = 0; missed < CACHE_SIZE; missed++) { + CACHE[getCacheIndex(covered, missed)] = new Coverage(covered, missed); + } + } + } + + private static int getCacheIndex(final int covered, final int missed) { + return covered * CACHE_SIZE + missed; + } + + /** Null object that indicates that the code coverage has not been measured. */ + public static final Coverage NO_COVERAGE = CACHE[0]; + + private int covered; + private boolean isCoveredSet; + private int missed; + private boolean isMissedSet; + private int total; + private boolean isTotalSet; + + /** + * Sets the number of total items. + * + * @param total + * the number of total items + * + * @return this + */ + public CoverageBuilder setTotal(final int total) { + this.total = total; + isTotalSet = true; + return this; + } + + /** + * Sets the number of covered items. + * + * @param covered + * the number of covered items + * + * @return this + */ + public CoverageBuilder setCovered(final int covered) { + this.covered = covered; + isCoveredSet = true; + return this; + } + + /** + * Sets the number of missed items. + * + * @param missed + * the number of missed items + * + * @return this + */ + public CoverageBuilder setMissed(final int missed) { + this.missed = missed; + isMissedSet = true; + return this; + } + + /** + * Creates the new {@link Coverage} instance. + * + * @return the new instance + */ + @SuppressWarnings("PMD.CyclomaticComplexity") + public Coverage build() { + if (isCoveredSet && isMissedSet && isTotalSet) { + throw new IllegalArgumentException( + "Setting all three values covered, missed, and total is not allowed, just select two of them."); + } + if (isTotalSet) { + if (isCoveredSet) { + return createOrGetCoverage(covered, total - covered); + } + else if (isMissedSet) { + return createOrGetCoverage(total - missed, missed); + } + } + else { + if (isCoveredSet && isMissedSet) { + return createOrGetCoverage(covered, missed); + } + } + throw new IllegalArgumentException("You must set exactly two properties."); + } + + @SuppressWarnings({"checkstyle:HiddenField", "ParameterHidesMemberVariable"}) + private Coverage createOrGetCoverage(final int covered, final int missed) { + if (covered < CACHE_SIZE && missed < CACHE_SIZE) { + return CACHE[getCacheIndex(covered, missed)]; + } + return new Coverage(covered, missed); + } + } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java index 597109938..7b199f2ee 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java @@ -7,12 +7,6 @@ import java.util.SortedMap; import java.util.TreeMap; -import com.thoughtworks.xstream.converters.Converter; -import com.thoughtworks.xstream.converters.MarshallingContext; -import com.thoughtworks.xstream.converters.UnmarshallingContext; -import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import com.thoughtworks.xstream.io.HierarchicalStreamWriter; - import edu.hm.hafner.util.VisibleForTesting; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; @@ -24,7 +18,6 @@ import hudson.model.HealthReport; import hudson.model.HealthReportingAction; import hudson.model.Run; -import hudson.util.XStream2; import io.jenkins.plugins.forensics.reference.ReferenceBuild; import io.jenkins.plugins.util.AbstractXmlStream; @@ -527,55 +520,4 @@ public String getUrlName() { return DETAILS_URL; } - /** - * {@link Converter} for {@link CoverageMetric} instances so that only the string name will be serialized. After - * reading the values back from the stream, the string representation will be converted to an actual instance - * again. - */ - private static final class MetricsConverter implements Converter { - @SuppressWarnings("PMD.NullAssignment") - @Override - public void marshal(final Object source, final HierarchicalStreamWriter writer, - final MarshallingContext context) { - writer.setValue(source instanceof CoverageMetric ? ((CoverageMetric) source).getName() : null); - } - - @Override - public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { - return CoverageMetric.valueOf(reader.getValue()); - } - - @Override - public boolean canConvert(final Class type) { - return type == CoverageMetric.class; - } - } - - /** - * Configures the XML stream for the coverage tree, which consists of {@link CoverageNode}. - */ - static class CoverageXmlStream extends AbstractXmlStream { - - /** - * Creates a XML stream for {@link CoverageNode}. - */ - CoverageXmlStream() { - super(CoverageNode.class); - } - - @Override - protected void configureXStream(final XStream2 xStream) { - xStream.alias("node", CoverageNode.class); - xStream.alias("leaf", CoverageLeaf.class); - xStream.alias("coverage", Coverage.class); - xStream.alias("coveragePercentage", CoveragePercentage.class); - xStream.addImmutableType(CoverageMetric.class, false); - xStream.registerConverter(new MetricsConverter()); - } - - @Override - protected CoverageNode createDefaultValue() { - return new CoverageNode(CoverageMetric.MODULE, "Empty"); - } - } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java index 22d5ffe5b..02aac73f7 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java @@ -3,6 +3,8 @@ import java.io.Serializable; import java.util.Objects; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + /** * A leaf in the coverage hierarchy. A leaf is a non-divisible coverage metric like line or branch coverage. * @@ -43,7 +45,7 @@ public Coverage getCoverage(final CoverageMetric searchMetric) { if (metric.equals(searchMetric)) { return coverage; } - return Coverage.NO_COVERAGE; + return CoverageBuilder.NO_COVERAGE; } @Override diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java index 57efb4a26..d11e43666 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java @@ -28,6 +28,8 @@ import edu.hm.hafner.util.VisibleForTesting; import edu.umd.cs.findbugs.annotations.CheckForNull; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + import one.util.streamex.StreamEx; /** @@ -39,8 +41,8 @@ public class CoverageNode implements Serializable { private static final long serialVersionUID = -6608885640271135273L; - private static final Coverage COVERED_NODE = new Coverage(1, 0); - private static final Coverage MISSED_NODE = new Coverage(0, 1); + private static final Coverage COVERED_NODE = new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build(); + private static final Coverage MISSED_NODE = new Coverage.CoverageBuilder().setCovered(0).setMissed(1).build(); /** Transient non static {@link CoverageTreeCreator} in order to be able to mock it for tests. */ private transient CoverageTreeCreator coverageTreeCreator; @@ -332,7 +334,7 @@ public Coverage getCoverage(final CoverageMetric searchMetric) { if (searchMetric.isLeaf()) { Coverage childrenCoverage = children.stream() .map(node -> node.getCoverage(searchMetric)) - .reduce(Coverage.NO_COVERAGE, Coverage::add); + .reduce(CoverageBuilder.NO_COVERAGE, Coverage::add); return leaves.stream() .map(node -> node.getCoverage(searchMetric)) .reduce(childrenCoverage, Coverage::add); @@ -340,7 +342,7 @@ public Coverage getCoverage(final CoverageMetric searchMetric) { else { Coverage childrenCoverage = children.stream() .map(node -> node.getCoverage(searchMetric)) - .reduce(Coverage.NO_COVERAGE, Coverage::add); + .reduce(CoverageBuilder.NO_COVERAGE, Coverage::add); if (metric.equals(searchMetric)) { if (getCoverage(CoverageMetric.LINE).getCovered() > 0) { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java index 1c720b16b..2b92ed4ce 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java @@ -42,7 +42,9 @@ private CoverageNode createNode(final CoverageResult result) { for (Map.Entry coverage : result.getLocalResults().entrySet()) { Ratio ratio = coverage.getValue(); CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.valueOf(coverage.getKey().getName()), - new Coverage((int) ratio.numerator, (int) (ratio.denominator - ratio.numerator))); + new Coverage.CoverageBuilder().setCovered((int) ratio.numerator) + .setMissed((int) (ratio.denominator - ratio.numerator)) + .build()); coverageNode.add(leaf); } return coverageNode; @@ -93,11 +95,14 @@ private void attachCoveragePerLine(final FileCoverageNode node, final CoveragePa if (paint.getBranchTotal(line) > 0) { int covered = paint.getBranchCoverage(line); int missed = paint.getBranchTotal(line) - covered; - coverageDetails.put(line, new Coverage(paint.getBranchCoverage(line), missed)); + coverageDetails.put(line, new Coverage.CoverageBuilder().setCovered(paint.getBranchCoverage(line)) + .setMissed(missed) + .build()); } else { int covered = paint.getHits(line) > 0 ? 1 : 0; - coverageDetails.put(line, new Coverage(covered, 1 - covered)); + coverageDetails.put(line, + new Coverage.CoverageBuilder().setCovered(covered).setMissed(1 - covered).build()); } } node.setCoveragePerLine(coverageDetails); diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java index 5488152bf..a02137c52 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java @@ -4,6 +4,7 @@ import java.util.Locale; import java.util.Objects; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.Fraction; /** @@ -14,27 +15,10 @@ * @author Florian Orendi */ public final class CoveragePercentage implements Serializable { - private static final long serialVersionUID = 3324942976687883481L; static final String DENOMINATOR_ZERO_MESSAGE = "The denominator must not be zero"; - private final int numerator; - private final int denominator; - - /** - * Creates an instance of {@link CoveragePercentage}. - * - * @param numerator - * The numerator of the fraction which represents the percentage - * @param denominator - * The denominator of the fraction which represents the percentage - */ - private CoveragePercentage(final int numerator, final int denominator) { - this.numerator = numerator; - this.denominator = denominator; - } - /** * Creates an instance of {@link CoveragePercentage} from a {@link Fraction fraction} within the range [0,1]. * @@ -75,10 +59,57 @@ public static CoveragePercentage valueOf(final double percentage) { * if the denominator is zero */ public static CoveragePercentage valueOf(final int numerator, final int denominator) { - if (denominator != 0) { - return new CoveragePercentage(numerator, denominator); + return new CoveragePercentage(numerator, denominator); + } + + /** + * Creates a new {@link CoveragePercentage} instance from the provided string representation. The string + * representation is expected to contain the numerator and the denominator - separated by a slash, e.g. "500/345", + * or "100/1". Whitespace characters will be ignored. + * + * @param stringRepresentation + * string representation to convert from + * + * @return the created {@link CoveragePercentage} + * @throws IllegalArgumentException + * if the string is not a valid CoveragePercentage instance + */ + public static CoveragePercentage valueOf(final String stringRepresentation) { + try { + String cleanedFormat = StringUtils.deleteWhitespace(stringRepresentation); + if (StringUtils.contains(cleanedFormat, "/")) { + String extractedNumerator = StringUtils.substringBefore(cleanedFormat, "/"); + String extractedDenominator = StringUtils.substringAfter(cleanedFormat, "/"); + + int numerator = Integer.parseInt(extractedNumerator); + int denominator = Integer.parseInt(extractedDenominator); + return new CoveragePercentage(numerator, denominator); + } + } + catch (NumberFormatException exception) { + // ignore and throw a specific exception } - throw new IllegalArgumentException(DENOMINATOR_ZERO_MESSAGE); + throw new IllegalArgumentException( + String.format("Cannot convert %s to a valid CoveragePercentage instance.", stringRepresentation)); + } + + private final int numerator; + private final int denominator; + + /** + * Creates an instance of {@link CoveragePercentage}. + * + * @param numerator + * The numerator of the fraction which represents the percentage + * @param denominator + * The denominator of the fraction which represents the percentage + */ + private CoveragePercentage(final int numerator, final int denominator) { + if (denominator == 0) { + throw new IllegalArgumentException(DENOMINATOR_ZERO_MESSAGE); + } + this.numerator = numerator; + this.denominator = denominator; } /** @@ -139,4 +170,20 @@ public boolean equals(final Object o) { public int hashCode() { return Objects.hash(numerator, denominator); } + + /** + * Returns a string representation for this {@link CoveragePercentage} that can be used to serialize this instance + * in a simple but still readable way. The serialization contains the numerator and the denominator - separated by a + * slash, e.g. "100/345", or "0/1". + * + * @return a string representation for this {@link CoveragePercentage} + */ + public String serializeToString() { + return String.format("%d/%d", getNumerator(), getDenominator()); + } + + @Override + public String toString() { + return formatPercentage(Locale.ENGLISH); + } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java index 821a31d88..0d6bb68ee 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java @@ -6,6 +6,8 @@ import java.util.Map.Entry; import java.util.stream.Collectors; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + /** * Creates coverage trees which represent different types of coverage. * @@ -152,17 +154,21 @@ private void attachIndirectCoverageChangesLeaves(final CoverageNode node) { * The {@link Coverage} to be represented by the leaves */ private void createChangeCoverageLeaves(final FileCoverageNode fileNode, final List changes) { - Coverage lineCoverage = Coverage.NO_COVERAGE; - Coverage branchCoverage = Coverage.NO_COVERAGE; + Coverage lineCoverage = CoverageBuilder.NO_COVERAGE; + Coverage branchCoverage = CoverageBuilder.NO_COVERAGE; for (Coverage change : changes) { int covered = change.getCovered() > 0 ? 1 : 0; if (change.getTotal() > 1) { - branchCoverage = branchCoverage.add(new Coverage(change.getCovered(), change.getMissed())); - lineCoverage = lineCoverage.add(new Coverage(covered, 1 - covered)); + branchCoverage = branchCoverage.add(new Coverage.CoverageBuilder().setCovered(change.getCovered()) + .setMissed(change.getMissed()) + .build()); + lineCoverage = lineCoverage.add( + new Coverage.CoverageBuilder().setCovered(covered).setMissed(1 - covered).build()); } else { int missed = change.getMissed() > 0 ? 1 : 0; - lineCoverage = lineCoverage.add(new Coverage(covered, missed)); + lineCoverage = lineCoverage.add( + new Coverage.CoverageBuilder().setCovered(covered).setMissed(missed).build()); } } if (lineCoverage.isSet()) { @@ -185,29 +191,31 @@ private void createChangeCoverageLeaves(final FileCoverageNode fileNode, final L @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CognitiveComplexity"}) // there is no useful possibility for outsourcing code private void createIndirectCoverageChangesLeaves(final FileCoverageNode fileNode) { - Coverage lineCoverage = Coverage.NO_COVERAGE; - Coverage branchCoverage = Coverage.NO_COVERAGE; + Coverage lineCoverage = CoverageBuilder.NO_COVERAGE; + Coverage branchCoverage = CoverageBuilder.NO_COVERAGE; for (Map.Entry change : fileNode.getIndirectCoverageChanges().entrySet()) { int delta = change.getValue(); Coverage currentCoverage = fileNode.getCoveragePerLine().get(change.getKey()); if (delta > 0) { // the line is fully covered - even in case of branch coverage if (delta == currentCoverage.getCovered()) { - lineCoverage = lineCoverage.add(new Coverage(1, 0)); + lineCoverage = lineCoverage.add(new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()); } // the branch coverage increased for 'delta' hits if (currentCoverage.getTotal() > 1) { - branchCoverage = branchCoverage.add(new Coverage(delta, 0)); + branchCoverage = branchCoverage.add( + new Coverage.CoverageBuilder().setCovered(delta).setMissed(0).build()); } } else if (delta < 0) { - // the line is not covered any more + // the line is not covered anymore if (currentCoverage.getCovered() == 0) { - lineCoverage = lineCoverage.add(new Coverage(0, 1)); + lineCoverage = lineCoverage.add(new Coverage.CoverageBuilder().setCovered(0).setMissed(1).build()); } // the branch coverage is decreased by 'delta' hits if (currentCoverage.getTotal() > 1) { - branchCoverage = branchCoverage.add(new Coverage(0, Math.abs(delta))); + branchCoverage = branchCoverage.add( + new Coverage.CoverageBuilder().setCovered(0).setMissed(Math.abs(delta)).build()); } } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java new file mode 100644 index 000000000..3e9092b85 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java @@ -0,0 +1,287 @@ +package io.jenkins.plugins.coverage.model; + +import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +import hudson.util.XStream2; + +import io.jenkins.plugins.util.AbstractXmlStream; + +/** + * Configures the XML stream for the coverage tree, which consists of {@link CoverageNode}s. + */ +class CoverageXmlStream extends AbstractXmlStream { + private static final Collector ARRAY_JOINER = Collectors.joining(", ", "[", "]"); + + private static String[] toArray(final String value) { + String cleanInput = StringUtils.removeEnd(StringUtils.removeStart(StringUtils.deleteWhitespace(value), "["), "]"); + + return StringUtils.split(cleanInput, ","); + } + + /** + * Creates an XML stream for {@link CoverageNode}. + */ + CoverageXmlStream() { + super(CoverageNode.class); + } + + @Override + protected void configureXStream(final XStream2 xStream) { + xStream.alias("node", CoverageNode.class); + xStream.alias("package", PackageCoverageNode.class); + xStream.alias("file", FileCoverageNode.class); + xStream.alias("method", MethodCoverageNode.class); + xStream.alias("leaf", CoverageLeaf.class); + xStream.alias("coverage", Coverage.class); + xStream.alias("percentage", CoveragePercentage.class); + xStream.addImmutableType(CoverageMetric.class, false); + xStream.addImmutableType(Coverage.class, false); + xStream.addImmutableType(CoveragePercentageConverter.class, false); + xStream.registerConverter(new CoverageMetricConverter()); + xStream.registerConverter(new CoverageConverter()); + xStream.registerConverter(new CoveragePercentageConverter()); + xStream.registerLocalConverter(FileCoverageNode.class, "coveragePerLine", new LineMapConverter()); + xStream.registerLocalConverter(FileCoverageNode.class, "fileCoverageDelta", new MetricPercentageMapConverter()); + xStream.registerLocalConverter(FileCoverageNode.class, "indirectCoverageChanges", new HitsMapConverter()); + xStream.registerLocalConverter(FileCoverageNode.class, "changedCodeLines", new IntegerSetConverter()); + } + + @Override + protected CoverageNode createDefaultValue() { + return new CoverageNode(CoverageMetric.MODULE, "Empty"); + } + + /** + * {@link Converter} for {@link CoverageMetric} instances so that only the string name will be serialized. After + * reading the values back from the stream, the string representation will be converted to an actual instance + * again. + */ + private static final class CoverageMetricConverter implements Converter { + @SuppressWarnings("PMD.NullAssignment") + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, + final MarshallingContext context) { + writer.setValue(source instanceof CoverageMetric ? ((CoverageMetric) source).getName() : null); + } + + @Override + public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + return CoverageMetric.valueOf(reader.getValue()); + } + + @Override + public boolean canConvert(final Class type) { + return type == CoverageMetric.class; + } + } + + /** + * {@link Converter} for {@link Coverage} instances so that only the values will be serialized. After reading the + * values back from the stream, the string representation will be converted to an actual instance again. + */ + private static final class CoverageConverter implements Converter { + @SuppressWarnings("PMD.NullAssignment") + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, + final MarshallingContext context) { + writer.setValue(source instanceof Coverage ? ((Coverage) source).serializeToString() : null); + } + + @Override + public Coverage unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + return Coverage.valueOf(reader.getValue()); + } + + @Override + public boolean canConvert(final Class type) { + return type == Coverage.class; + } + } + + /** + * {@link Converter} for {@link CoveragePercentage} instances so that only the values will be serialized. After + * reading the values back from the stream, the string representation will be converted to an actual instance + * again. + */ + private static final class CoveragePercentageConverter implements Converter { + @SuppressWarnings("PMD.NullAssignment") + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, + final MarshallingContext context) { + writer.setValue( + source instanceof CoveragePercentage ? ((CoveragePercentage) source).serializeToString() : null); + } + + @Override + public CoveragePercentage unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + return CoveragePercentage.valueOf(reader.getValue()); + } + + @Override + public boolean canConvert(final Class type) { + return type == CoveragePercentage.class; + } + } + + /** + * {@link Converter} for a {@link TreeSet} of integers that serializes just the values. After + * reading the values back from the stream, the string representation will be converted to an actual instance + * again. + */ + static final class IntegerSetConverter implements Converter { + @SuppressWarnings({"PMD.NullAssignment", "unchecked"}) + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, + final MarshallingContext context) { + writer.setValue(source instanceof TreeSet ? marshal((TreeSet) source) : null); + } + + String marshal(final Set lines) { + return lines.stream().map(String::valueOf).collect(ARRAY_JOINER); + } + + @Override + public NavigableSet unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + return unmarshal(reader.getValue()); + } + + NavigableSet unmarshal(final String value) { + return Arrays.stream(toArray(value)).map(Integer::valueOf).collect(Collectors.toCollection(TreeSet::new)); + } + + @Override + public boolean canConvert(final Class type) { + return type == TreeSet.class; + } + } + + /** + * {@link Converter} base class for {@link TreeMap} instance. Stores the mappings in a condensed format + * {@code key1: value1, key2: value2, ...}. + * + * @param + * the type of keys maintained by this map + * @param + * the type of mapped values + */ + abstract static class TreeMapConverter, V> implements Converter { + @Override + @SuppressWarnings({"PMD.NullAssignment", "unchecked"}) + public void marshal(final Object source, final HierarchicalStreamWriter writer, + final MarshallingContext context) { + writer.setValue(source instanceof NavigableMap ? marshal((NavigableMap) source) : null); + } + + String marshal(final SortedMap source) { + return source.entrySet() + .stream() + .map(createMapEntry()) + .collect(ARRAY_JOINER); + } + + @Override + public boolean canConvert(final Class type) { + return type == TreeMap.class; + } + + @Override + public NavigableMap unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + return unmarshal(reader.getValue()); + } + + NavigableMap unmarshal(final String value) { + NavigableMap map = new TreeMap<>(); + + for (String marshalledValue : toArray(value)) { + if (StringUtils.contains(marshalledValue, ":")) { + try { + Entry entry = createMapping( + StringUtils.substringBefore(marshalledValue, ':'), + StringUtils.substringAfter(marshalledValue, ':')); + map.put(entry.getKey(), entry.getValue()); + } + catch (IllegalArgumentException exception) { + // ignore + } + } + } + return map; + } + + protected abstract Function, String> createMapEntry(); + + protected abstract Map.Entry createMapping(String key, String value); + + protected SimpleEntry entry(final K key, final V value) { + return new SimpleEntry<>(key, value); + } + } + + /** + * {@link Converter} for a {@link SortedMap} of coverages per line. Stores the mapping in the condensed format + * {@code key1: covered1/missed1, key2: covered2/missed2, ...}. + */ + static final class LineMapConverter extends TreeMapConverter { + @Override + protected Function, String> createMapEntry() { + return e -> String.format("%d: %s", e.getKey(), e.getValue().serializeToString()); + } + + @Override + protected Entry createMapping(final String key, final String value) { + return entry(Integer.valueOf(key), Coverage.valueOf(value)); + } + } + + /** + * {@link Converter} for a {@link SortedMap} of coverage percentages per metric. Stores the mapping in the condensed + * format {@code metric1: numerator1/denominator1, metric2: numerator2/denominator2, ...}. + */ + static final class MetricPercentageMapConverter extends TreeMapConverter { + @Override + protected Function, String> createMapEntry() { + return e -> String.format("%s: %s", e.getKey().getName(), e.getValue().serializeToString()); + } + + @Override + protected Entry createMapping(final String key, final String value) { + return entry(CoverageMetric.valueOf(key), CoveragePercentage.valueOf(value)); + } + } + + /** + * {@link Converter} for a {@link SortedMap} of coverage hits per line. Stores the mapping in the condensed + * format {@code line1: hits1, line2: hits2, ...}. + */ + static final class HitsMapConverter extends TreeMapConverter { + @Override + protected Function, String> createMapEntry() { + return e -> String.format("%d: %d", e.getKey(), e.getValue()); + } + + @Override + protected Entry createMapping(final String key, final String value) { + return entry(Integer.valueOf(key), Integer.valueOf(value)); + } + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java index ab47ae095..1307f74bd 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java @@ -21,15 +21,14 @@ public class FileCoverageNode extends CoverageNode { private final String sourcePath; - // new since 3.0.0 /** * The {@link Coverage} represents both line and branch coverage per line since it can be differentiated by the * total number of covered and missed cases and saves disk space. */ - private SortedMap coveragePerLine = new TreeMap<>(); - private SortedMap fileCoverageDelta = new TreeMap<>(); - private SortedMap indirectCoverageChanges = new TreeMap<>(); - private SortedSet changedCodeLines = new TreeSet<>(); + private SortedMap coveragePerLine = new TreeMap<>(); // since 3.0.0 + private SortedMap fileCoverageDelta = new TreeMap<>(); // since 3.0.0 + private SortedMap indirectCoverageChanges = new TreeMap<>(); // since 3.0.0 + private SortedSet changedCodeLines = new TreeSet<>(); // since 3.0.0 /** * Creates a new {@link FileCoverageNode} with the given name. diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java index 31bac76b9..d6963f3e0 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java @@ -97,8 +97,8 @@ private CoverageBuildAction creataBuildAction(final FreeStyleBuild build) { CoverageBuildAction action = mock(CoverageBuildAction.class); when(action.getOwner()).thenAnswer(i -> build); when(action.getUrlName()).thenReturn("coverage"); - when(action.getBranchCoverage()).thenReturn(new Coverage(9, 1)); - when(action.getLineCoverage()).thenReturn(new Coverage(10, 10)); + when(action.getBranchCoverage()).thenReturn(new Coverage.CoverageBuilder().setCovered(9).setMissed(1).build()); + when(action.getLineCoverage()).thenReturn(new Coverage.CoverageBuilder().setCovered(10).setMissed(10).build()); return action; } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java index cd372dd69..26529470e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java @@ -4,6 +4,8 @@ import nl.jqno.equalsverifier.EqualsVerifier; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + import static io.jenkins.plugins.coverage.model.Assertions.*; /** @@ -12,7 +14,7 @@ * @author Ullrich Hafner */ class CoverageLeafTest extends AbstractCoverageTest { - private static final Coverage COVERED = new Coverage(1, 0); + private static final Coverage COVERED = new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build(); @Test void shouldCreateLeaf() { @@ -20,7 +22,7 @@ void shouldCreateLeaf() { assertThat(coverageLeaf).hasMetric(LINE).hasToString("[Line]: 100.00% (1/1)"); assertThat(coverageLeaf.getCoverage(LINE)).isEqualTo(COVERED); - assertThat(coverageLeaf.getCoverage(CoverageMetric.MODULE)).isEqualTo(Coverage.NO_COVERAGE); + assertThat(coverageLeaf.getCoverage(CoverageMetric.MODULE)).isEqualTo(CoverageBuilder.NO_COVERAGE); } @Test diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java index 3033178fd..bbf62f891 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java @@ -11,6 +11,8 @@ import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + import static io.jenkins.plugins.coverage.model.Assertions.*; import static org.mockito.Mockito.*; @@ -95,14 +97,14 @@ void shouldConvertCodingStyleToTree() { assertThat(tree).hasOnlyMetrics(MODULE, PACKAGE, FILE, CLASS, METHOD, LINE, BRANCH, INSTRUCTION) .hasToString("[Module] " + PROJECT_NAME); assertThat(tree.getMetricsDistribution()).containsExactly( - entry(MODULE, new Coverage(1, 0)), - entry(PACKAGE, new Coverage(1, 0)), - entry(FILE, new Coverage(7, 3)), - entry(CLASS, new Coverage(15, 3)), - entry(METHOD, new Coverage(97, 5)), - entry(LINE, new Coverage(294, 29)), - entry(INSTRUCTION, new Coverage(1260, 90)), - entry(BRANCH, new Coverage(109, 7))); + entry(MODULE, new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()), + entry(PACKAGE, new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()), + entry(FILE, new Coverage.CoverageBuilder().setCovered(7).setMissed(3).build()), + entry(CLASS, new Coverage.CoverageBuilder().setCovered(15).setMissed(3).build()), + entry(METHOD, new Coverage.CoverageBuilder().setCovered(97).setMissed(5).build()), + entry(LINE, new Coverage.CoverageBuilder().setCovered(294).setMissed(29).build()), + entry(INSTRUCTION, new Coverage.CoverageBuilder().setCovered(1260).setMissed(90).build()), + entry(BRANCH, new Coverage.CoverageBuilder().setCovered(109).setMissed(7).build())); assertThat(tree.getMetricFractions()).containsExactly( entry(MODULE, Fraction.ONE), entry(PACKAGE, Fraction.ONE), @@ -210,7 +212,7 @@ void shouldSplitPackages() { assertThat(tree.getAll(PACKAGE)).hasSize(4); assertThat(tree.getMetricsDistribution()).contains( - entry(PACKAGE, new Coverage(4, 0))); + entry(PACKAGE, new Coverage.CoverageBuilder().setCovered(4).setMissed(0).build())); assertThat(tree.getChildren()).hasSize(1).element(0).satisfies( packageNode -> assertThat(packageNode).hasName("edu") @@ -498,7 +500,7 @@ private CoverageNode createTreeWithMockedTreeCreator() { CoverageNode root = new CoverageNode(CoverageMetric.MODULE, CoverageNode.ROOT, coverageTreeCreator); FileCoverageNode fileNode = new FileCoverageNode("test", "test"); - fileNode.putCoveragePerLine(1, Coverage.NO_COVERAGE); + fileNode.putCoveragePerLine(1, CoverageBuilder.NO_COVERAGE); fileNode.addChangedCodeLine(1); fileNode.addChangedCodeLine(2); fileNode.putIndirectCoverageChange(3, 1); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java index e41e4d634..d6a9f06c7 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java @@ -7,6 +7,7 @@ import nl.jqno.equalsverifier.EqualsVerifier; +import static io.jenkins.plugins.coverage.model.Assertions.assertThat; import static io.jenkins.plugins.coverage.model.CoveragePercentage.*; import static org.assertj.core.api.Assertions.*; @@ -16,7 +17,6 @@ * @author Florian Orendi */ class CoveragePercentageTest { - private static final double COVERAGE_FRACTION = 0.5; private static final double COVERAGE_PERCENTAGE = 50.0; private static final Locale LOCALE = Locale.GERMAN; @@ -76,4 +76,16 @@ void shouldFormatDeltaPercentage() { void shouldObeyEqualsContract() { EqualsVerifier.forClass(CoveragePercentage.class).verify(); } + + @Test + void shouldSerializeInstance() { + CoveragePercentage percentage = valueOf(49, 1); + assertThat(percentage.serializeToString()) + .isEqualTo("49/1"); + assertThat(valueOf("49/1")).isEqualTo(percentage) + .hasToString("49.00%"); + + assertThatIllegalArgumentException().isThrownBy( + () -> Coverage.valueOf("1/0")); + } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index f86fff058..a9d118392 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -342,7 +342,9 @@ private void verifyForOneJacoco(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage(JACOCO_COVERED_LINES, JACOCO_ALL_LINES - JACOCO_COVERED_LINES)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_COVERED_LINES) + .setMissed(JACOCO_ALL_LINES - JACOCO_COVERED_LINES) + .build()); } /** @@ -356,7 +358,9 @@ private void verifyForTwoJacoco(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage(BOTH_JACOCO_COVERED_LINES, BOTH_JACOCO_ALL_LINES - BOTH_JACOCO_COVERED_LINES)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(BOTH_JACOCO_COVERED_LINES) + .setMissed(BOTH_JACOCO_ALL_LINES - BOTH_JACOCO_COVERED_LINES) + .build()); } /** @@ -392,7 +396,9 @@ private void verifyForOneCobertura(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage(COBERTURA_COVERED_LINES, COBERTURA_ALL_LINES - COBERTURA_COVERED_LINES)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(COBERTURA_COVERED_LINES) + .setMissed(COBERTURA_ALL_LINES - COBERTURA_COVERED_LINES) + .build()); } @@ -406,7 +412,8 @@ private void verifyForTwoCobertura(final ParameterizedJob project) { Run build = buildSuccessfully(project); CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); //FIXME - assertThat(coverageResult.getLineCoverage()).isEqualTo(new Coverage(472, 722 - 472)); + assertThat(coverageResult.getLineCoverage()).isEqualTo( + new Coverage.CoverageBuilder().setCovered(472).setMissed(722 - 472).build()); } /** @@ -420,8 +427,9 @@ private void verifyForOneCoberturaAndOneJacoco(final ParameterizedJob proj CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage(JACOCO_COBERTURA_COVERED_LINES, - JACOCO_COBERTURA_ALL_LINES - JACOCO_COBERTURA_COVERED_LINES)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_COBERTURA_COVERED_LINES) + .setMissed(JACOCO_COBERTURA_ALL_LINES - JACOCO_COBERTURA_COVERED_LINES) + .build()); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java index 892a0bd30..74b51715f 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java @@ -165,7 +165,7 @@ private void verifySourceCodeInBuild(final Run build, final String sourceC private CoverageViewModel verifyViewModel(final Run build) { CoverageBuildAction action = build.getAction(CoverageBuildAction.class); assertThat(action.getLineCoverage()) - .isEqualTo(new Coverage(8, 0)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(8).setMissed(0).build()); Optional fileNode = action.getResult().find(CoverageMetric.FILE, SOURCE_FILE_NAME); assertThat(fileNode).isNotEmpty() @@ -196,7 +196,7 @@ void verifySimpleCoverageNode(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage(6083, 6368 - 6083)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(6083).setMissed(6368 - 6083).build()); System.out.println(getConsoleLog(build)); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java index 30b49ef35..2c2685c7e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java @@ -4,11 +4,17 @@ import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import nl.jqno.equalsverifier.EqualsVerifier; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + import static io.jenkins.plugins.coverage.model.Assertions.*; +import static io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder.*; /** * Tests the class {@link Coverage}. @@ -23,7 +29,7 @@ static void beforeAll() { @Test void shouldProvideNullObject() { - assertThat(Coverage.NO_COVERAGE).isNotSet() + assertThat(NO_COVERAGE).isNotSet() .hasCovered(0) .hasCoveredFraction(Fraction.ZERO) .hasCoveredPercentage(CoveragePercentage.valueOf(Fraction.ZERO)) @@ -33,14 +39,17 @@ void shouldProvideNullObject() { .hasMissedPercentage(CoveragePercentage.valueOf(Fraction.ZERO)) .hasTotal(0) .hasToString(Messages.Coverage_Not_Available()); - assertThat(Coverage.NO_COVERAGE.formatCoveredPercentage()).isEqualTo(Messages.Coverage_Not_Available()); - assertThat(Coverage.NO_COVERAGE.formatMissedPercentage()).isEqualTo(Messages.Coverage_Not_Available()); - assertThat(Coverage.NO_COVERAGE.add(Coverage.NO_COVERAGE)).isEqualTo(Coverage.NO_COVERAGE); + assertThat(NO_COVERAGE.formatCoveredPercentage()).isEqualTo(Messages.Coverage_Not_Available()); + assertThat(NO_COVERAGE.formatMissedPercentage()).isEqualTo(Messages.Coverage_Not_Available()); + assertThat(NO_COVERAGE.add(NO_COVERAGE)).isEqualTo(NO_COVERAGE); + + assertThat(NO_COVERAGE.serializeToString()).isEqualTo("0/0"); + assertThat(Coverage.valueOf("0/0")).isEqualTo(NO_COVERAGE); } @Test void shouldCreatePercentages() { - Coverage coverage = new Coverage(6, 4); + Coverage coverage = new CoverageBuilder().setCovered(6).setMissed(4).build(); Fraction coverageFraction = Fraction.getFraction(6, 10); Fraction missedFraction = Fraction.getFraction(4, 10); assertThat(coverage).isSet() @@ -54,16 +63,78 @@ void shouldCreatePercentages() { .hasTotal(10) .hasToString("60.00% (6/10)"); + assertThat(coverage.serializeToString()).isEqualTo("6/10"); + assertThat(Coverage.valueOf("6/10")).isEqualTo(coverage); + assertThat(coverage.formatCoveredPercentage()).isEqualTo("60.00%"); assertThat(coverage.formatMissedPercentage()).isEqualTo("40.00%"); - assertThat(coverage.add(Coverage.NO_COVERAGE)).isEqualTo(coverage); - Coverage sum = coverage.add(new Coverage(10, 0)); - assertThat(sum).isEqualTo(new Coverage(16, 4)).hasRoundedPercentage(80); + assertThat(coverage.add(NO_COVERAGE)).isEqualTo(coverage); + Coverage sum = coverage.add(new CoverageBuilder().setCovered(10).setMissed(0).build()); + assertThat(sum).isEqualTo(new CoverageBuilder().setCovered(16).setMissed(4).build()).hasRoundedPercentage(80); assertThat(sum.formatCoveredPercentage()).isEqualTo("80.00%"); assertThat(sum.formatMissedPercentage()).isEqualTo("20.00%"); } + @Test + void shouldThrowExceptionForInvalidBuilderArguments() { + assertThatIllegalArgumentException().isThrownBy(() -> + new CoverageBuilder().setCovered(1).setMissed(1).setTotal(1).build()); + assertThatIllegalArgumentException().isThrownBy(() -> + new CoverageBuilder().setCovered(1).build()); + assertThatIllegalArgumentException().isThrownBy(() -> + new CoverageBuilder().setMissed(1).build()); + assertThatIllegalArgumentException().isThrownBy(() -> + new CoverageBuilder().setTotal(1).build()); + } + + @Test + void shouldProvideMultipleOptionsToCreateCoverage() { + assertThat(new CoverageBuilder().setCovered(1).setMissed(2).build()) + .hasCovered(1) + .hasMissed(2) + .hasTotal(3); + assertThat(new CoverageBuilder().setCovered(1).setTotal(3).build()) + .hasCovered(1) + .hasMissed(2) + .hasTotal(3); + assertThat(new CoverageBuilder().setMissed(2).setTotal(3).build()) + .hasCovered(1) + .hasMissed(2) + .hasTotal(3); + } + + @Test + void shouldCacheValues() { + for (int covered = 0; covered < CACHE_SIZE; covered++) { + for (int missed = 0; missed < CACHE_SIZE; missed++) { + CoverageBuilder builder = new CoverageBuilder().setCovered(covered).setMissed(missed); + + assertThat(builder.build()) + .isSameAs(builder.build()) + .hasCovered(covered) + .hasMissed(missed); + } + } + + CoverageBuilder coveredOutOfCache = new CoverageBuilder().setCovered(CACHE_SIZE).setMissed(CACHE_SIZE - 1); + assertThat(coveredOutOfCache.build()) + .isNotSameAs(coveredOutOfCache.build()) + .isEqualTo(coveredOutOfCache.build()); + CoverageBuilder missedOutOfCache = new CoverageBuilder().setCovered(CACHE_SIZE - 1).setMissed(CACHE_SIZE); + assertThat(missedOutOfCache.build()) + .isNotSameAs(missedOutOfCache.build()) + .isEqualTo(missedOutOfCache.build()); + } + + @ParameterizedTest(name = "[{index}] Illegal coverage serialization = \"{0}\"") + @ValueSource(strings = {"", "-", "/", "0/", "0/0/0", "/0", "a/1", "1/a", "1.0/1.0", "4/3"}) + @DisplayName("Should throw exception for illegal serializations") + void shouldThrowExceptionForInvalidCoverages(final String serialization) { + assertThatIllegalArgumentException().isThrownBy(() -> Coverage.valueOf(serialization)) + .withMessageContaining(serialization); + } + @Test void shouldVerifyEquals() { EqualsVerifier.forClass(Coverage.class).verify(); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java index b5ee76cce..ee82a3e59 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java @@ -64,8 +64,10 @@ void shouldCreateChangeCoverageTree() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getAll(FILE)).hasSize(1); - assertThat(root.getCoverage(LINE)).isEqualTo(new Coverage(2, 2)); - assertThat(root.getCoverage(BRANCH)).isEqualTo(new Coverage(4, 4)); + assertThat(root.getCoverage(LINE)).isEqualTo( + new Coverage.CoverageBuilder().setCovered(2).setMissed(2).build()); + assertThat(root.getCoverage(BRANCH)).isEqualTo( + new Coverage.CoverageBuilder().setCovered(4).setMissed(4).build()); }); } @@ -82,8 +84,10 @@ void shouldCreateIndirectCoverageChangesTree() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getAll(FILE)).hasSize(1); - assertThat(root.getCoverage(LINE)).isEqualTo(new Coverage(2, 2)); - assertThat(root.getCoverage(BRANCH)).isEqualTo(new Coverage(4, 4)); + assertThat(root.getCoverage(LINE)).isEqualTo( + new Coverage.CoverageBuilder().setCovered(2).setMissed(2).build()); + assertThat(root.getCoverage(BRANCH)).isEqualTo( + new Coverage.CoverageBuilder().setCovered(4).setMissed(4).build()); }); } @@ -158,10 +162,10 @@ private FileCoverageNode attachFileCoverageNodeToTree(final CoverageNode root) { */ private void attachCoveragePerLine(final FileCoverageNode file) { SortedMap coveragePerLine = new TreeMap<>(); - coveragePerLine.put(10, new Coverage(1, 0)); - coveragePerLine.put(11, new Coverage(0, 4)); - coveragePerLine.put(12, new Coverage(4, 0)); - coveragePerLine.put(13, new Coverage(0, 1)); + coveragePerLine.put(10, new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()); + coveragePerLine.put(11, new Coverage.CoverageBuilder().setCovered(0).setMissed(4).build()); + coveragePerLine.put(12, new Coverage.CoverageBuilder().setCovered(4).setMissed(0).build()); + coveragePerLine.put(13, new Coverage.CoverageBuilder().setCovered(0).setMissed(1).build()); file.setCoveragePerLine(coveragePerLine); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java index 83d7c2e8a..f03f5e81e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java @@ -8,6 +8,8 @@ import hudson.model.Run; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + import static io.jenkins.plugins.coverage.model.Assertions.*; import static io.jenkins.plugins.coverage.model.CoverageViewModel.*; import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; @@ -101,10 +103,10 @@ private CoverageViewModel createModelFromMock(final CoverageNode mock) { when(mock.filterPackageStructure()).thenReturn(mock); SortedMap changeMetricsDistribution = new TreeMap<>(); - changeMetricsDistribution.put(LINE, Coverage.NO_COVERAGE); - changeMetricsDistribution.put(BRANCH, Coverage.NO_COVERAGE); - changeMetricsDistribution.put(FILE, Coverage.NO_COVERAGE); - changeMetricsDistribution.put(PACKAGE, Coverage.NO_COVERAGE); + changeMetricsDistribution.put(LINE, CoverageBuilder.NO_COVERAGE); + changeMetricsDistribution.put(BRANCH, CoverageBuilder.NO_COVERAGE); + changeMetricsDistribution.put(FILE, CoverageBuilder.NO_COVERAGE); + changeMetricsDistribution.put(PACKAGE, CoverageBuilder.NO_COVERAGE); when(mock.getMetricsDistribution()).thenReturn(changeMetricsDistribution); return new CoverageViewModel(mock(Run.class), mock); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java index 3360400c8..ec98b9fc6 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java @@ -1,6 +1,10 @@ package io.jenkins.plugins.coverage.model; import java.nio.file.Path; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.TreeMap; +import java.util.TreeSet; import org.junit.jupiter.api.Test; @@ -9,7 +13,11 @@ import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter.JacocoReportAdapterDescriptor; import io.jenkins.plugins.coverage.exception.CoverageException; -import io.jenkins.plugins.coverage.model.CoverageBuildAction.CoverageXmlStream; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; +import io.jenkins.plugins.coverage.model.CoverageXmlStream.HitsMapConverter; +import io.jenkins.plugins.coverage.model.CoverageXmlStream.IntegerSetConverter; +import io.jenkins.plugins.coverage.model.CoverageXmlStream.LineMapConverter; +import io.jenkins.plugins.coverage.model.CoverageXmlStream.MetricPercentageMapConverter; import io.jenkins.plugins.coverage.targets.CoverageElementRegister; import io.jenkins.plugins.coverage.targets.CoverageResult; @@ -21,6 +29,9 @@ * @author Ullrich Hafner */ class CoverageXmlStreamTest extends SerializableTest { + private static final CoverageBuilder BUILDER = new CoverageBuilder(); + private static final String EMPTY = "[]"; + @Override protected CoverageNode createSerializable() { // TODO: replace with actual result @@ -48,4 +59,106 @@ void shouldSaveAndRestoreTree() { CoverageNode restored = xmlStream.read(saved); assertThat(restored).isEqualTo(convertedNode); } + + @Test + void shouldConvertMap2String() { + NavigableMap map = new TreeMap<>(); + + LineMapConverter converter = new LineMapConverter(); + + assertThat(converter.marshal(map)).isEqualTo(EMPTY); + + map.put(10, BUILDER.setCovered(5).setTotal(8).build()); + assertThat(converter.marshal(map)).isEqualTo("[10: 5/8]"); + + map.put(20, BUILDER.setCovered(6).setTotal(6).build()); + assertThat(converter.marshal(map)).isEqualTo("[10: 5/8, 20: 6/6]"); + } + + @Test + void shouldConvertString2Map() { + LineMapConverter converter = new LineMapConverter(); + + assertThat(converter.unmarshal(EMPTY)).isEmpty(); + Coverage first = BUILDER.setCovered(5).setTotal(8).build(); + assertThat(converter.unmarshal("[10: 5/8]")) + .containsExactly(entry(10, first)); + assertThat(converter.unmarshal("[10: 5/8, 20: 6/6]")) + .containsExactly(entry(10, first), entry(20, BUILDER.setCovered(6).setTotal(6).build())); + } + + @Test + void shouldConvertMetricMap2String() { + NavigableMap map = new TreeMap<>(); + + MetricPercentageMapConverter converter = new MetricPercentageMapConverter(); + + assertThat(converter.marshal(map)).isEqualTo(EMPTY); + + map.put(CoverageMetric.BRANCH, CoveragePercentage.valueOf(50, 1)); + assertThat(converter.marshal(map)).isEqualTo("[Branch: 50/1]"); + + map.put(CoverageMetric.LINE, CoveragePercentage.valueOf(3, 4)); + assertThat(converter.marshal(map)).isEqualTo("[Line: 3/4, Branch: 50/1]"); + } + + @Test + void shouldConvertString2MetricMap() { + MetricPercentageMapConverter converter = new MetricPercentageMapConverter(); + + assertThat(converter.unmarshal(EMPTY)).isEmpty(); + CoveragePercentage first = CoveragePercentage.valueOf(50, 1); + assertThat(converter.unmarshal("[Branch: 50/1]")) + .containsExactly(entry(CoverageMetric.BRANCH, first)); + assertThat(converter.unmarshal("[Line: 3/4, Branch: 50/1]")) + .containsExactly(entry(CoverageMetric.LINE, CoveragePercentage.valueOf(3, 4)), entry(CoverageMetric.BRANCH, first)); + } + + @Test + void shouldConvertIntegerMap2String() { + NavigableMap map = new TreeMap<>(); + + HitsMapConverter converter = new HitsMapConverter(); + + assertThat(converter.marshal(map)).isEqualTo(EMPTY); + + map.put(10, 20); + assertThat(converter.marshal(map)).isEqualTo("[10: 20]"); + + map.put(15, 25); + assertThat(converter.marshal(map)).isEqualTo("[10: 20, 15: 25]"); + } + + @Test + void shouldConvertString2IntegerMap() { + HitsMapConverter converter = new HitsMapConverter(); + + assertThat(converter.unmarshal(EMPTY)).isEmpty(); + assertThat(converter.unmarshal("[15: 25]")).containsExactly(entry(15, 25)); + assertThat(converter.unmarshal("[15:25, 10: 20]")).containsExactly(entry(10, 20), entry(15, 25)); + } + + @Test + void shouldConvertIntegerSet2String() { + NavigableSet set = new TreeSet<>(); + + IntegerSetConverter converter = new IntegerSetConverter(); + + assertThat(converter.marshal(set)).isEqualTo(EMPTY); + + set.add(10); + assertThat(converter.marshal(set)).isEqualTo("[10]"); + + set.add(15); + assertThat(converter.marshal(set)).isEqualTo("[10, 15]"); + } + + @Test + void shouldConvertString2IntegerSet() { + IntegerSetConverter converter = new IntegerSetConverter(); + + assertThat(converter.unmarshal(EMPTY)).isEmpty(); + assertThat(converter.unmarshal("[15]")).containsExactly(15); + assertThat(converter.unmarshal("[15, 20]")).containsExactly(15, 20); + } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java index ff09b475b..0da3e6e07 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java @@ -38,7 +38,7 @@ void declarativePipelineSupportJacoco() { + "}", true)); Run build = buildSuccessfully(job); assertThat(build.getAction(CoverageBuildAction.class).getLineCoverage()) - .isEqualTo(new Coverage(6083, 6368 - 6083)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(6083).setMissed(6368 - 6083).build()); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java index f07ef69b3..3735b5873 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java @@ -66,7 +66,7 @@ void shouldCopyAndRenderSourceCodeAndRenderingInFreestyleJobOnAgent() throws IOE CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage(6083, 6368 - 6083)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(6083).setMissed(6368 - 6083).build()); } private void verifySourceCode(final Run build) { diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java index 7f7f340c1..cfb09e256 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java @@ -20,7 +20,7 @@ class FileCoverageNodeTest { private static final String PATH = "path"; private static final int LINE = 5; private static final int HIT_DELTA = 10; - private static final Coverage COVERAGE = new Coverage(2, 3); + private static final Coverage COVERAGE = new Coverage.CoverageBuilder().setCovered(2).setMissed(3).build(); private static final CoveragePercentage COVERAGE_DELTA = CoveragePercentage.valueOf(0.5); private static final CoverageMetric COVERAGE_METRIC = CoverageMetric.LINE; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java index 9436c2192..91c2afe36 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java @@ -5,6 +5,8 @@ import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + import static org.assertj.core.api.Assertions.*; /** @@ -56,7 +58,7 @@ void shouldObeyEqualsContract() { */ private MethodCoverageNode createMethodCoverageNode() { CoverageNode parent = new CoverageNode(CoverageMetric.LINE, ""); - CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, Coverage.NO_COVERAGE); + CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, CoverageBuilder.NO_COVERAGE); MethodCoverageNode node = new MethodCoverageNode(NAME, LINE); node.setParent(parent); parent.add(node); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java index 0b76a580d..98546ba33 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java @@ -39,8 +39,9 @@ void withoutTagFirstHigherFile() { List buildAction = getCoverageBuildActions(job, 1); assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE, - JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE) + .setMissed(JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE) + .build()); } @@ -66,8 +67,9 @@ void withoutTagFirstLowerFile() { List buildAction = getCoverageBuildActions(job, 1); assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE, - JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE) + .setMissed(JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE) + .build()); } /** @@ -80,11 +82,13 @@ void withDifferentTag() { List buildAction = getCoverageBuildActions(job, 2); assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE, - JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE) + .setMissed(JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE) + .build()); assertThat(buildAction.get(1).getBranchCoverage()) - .isEqualTo(new Coverage(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE, - JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE) + .setMissed(JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE) + .build()); } /** @@ -96,8 +100,9 @@ void witSameTag() { List buildAction = getCoverageBuildActions(job, 1); assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE, - JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE)); + .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE) + .setMissed(JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE) + .build()); } /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java index fa0e88bd6..629cacdfe 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java @@ -5,6 +5,8 @@ import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; + import static org.assertj.core.api.Assertions.*; /** @@ -49,7 +51,7 @@ void shouldObeyEqualsContract() { */ private PackageCoverageNode createPackageCoverageNode() { CoverageNode parent = new CoverageNode(CoverageMetric.MODULE, ""); - CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, Coverage.NO_COVERAGE); + CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, CoverageBuilder.NO_COVERAGE); PackageCoverageNode node = new PackageCoverageNode(PACKAGE); node.setParent(parent); parent.add(node); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java index 1dfc0b9bf..f551ae708 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java @@ -11,7 +11,7 @@ import edu.hm.hafner.echarts.LinesChartModel; import edu.hm.hafner.echarts.LinesDataSet; -import io.jenkins.plugins.coverage.model.Coverage; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; import io.jenkins.plugins.coverage.model.CoverageBuildAction; import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; @@ -38,13 +38,17 @@ void shouldHaveEmptyDataSetForEmptyIterator() { void shouldCreateChart() { CoverageTrendChart trendChart = new CoverageTrendChart(); - BuildResult smallLineCoverage = createResult(1, new Coverage(1, 1), new Coverage(3, 1)); + BuildResult smallLineCoverage = createResult(1, + new CoverageBuilder().setCovered(1).setMissed(1).build(), + new CoverageBuilder().setCovered(3).setMissed(1).build()); LinesChartModel lineCoverage = trendChart.create(Collections.singletonList(smallLineCoverage), createConfiguration()); verifySeriesDetails(lineCoverage); - BuildResult smallBranchCoverage = createResult(1, new Coverage(3, 1), new Coverage(1, 1)); + BuildResult smallBranchCoverage = createResult(1, + new CoverageBuilder().setCovered(3).setMissed(1).build(), + new CoverageBuilder().setCovered(1).setMissed(1).build()); LinesChartModel branchCoverage = trendChart.create(Collections.singletonList(smallBranchCoverage), createConfiguration()); @@ -62,7 +66,9 @@ private void verifySeriesDetails(final LinesChartModel lineCoverage) { void shouldHaveTwoValuesForSingleBuild() { CoverageSeriesBuilder builder = new CoverageSeriesBuilder(); - BuildResult singleResult = createResult(1, new Coverage(1, 1), new Coverage(3, 1)); + BuildResult singleResult = createResult(1, + new CoverageBuilder().setCovered(1).setMissed(1).build(), + new CoverageBuilder().setCovered(3).setMissed(1).build()); LinesDataSet dataSet = builder.createDataSet(createConfiguration(), Collections.singletonList(singleResult)); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java index 867ed596c..d3017da8a 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java @@ -16,6 +16,7 @@ import edu.hm.hafner.util.ResourceTest; import io.jenkins.plugins.coverage.model.Coverage; +import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; import io.jenkins.plugins.coverage.model.FileCoverageNode; import static org.assertj.core.api.Assertions.*; @@ -61,7 +62,7 @@ static void init() { INDIRECT_COVERAGE_CHANGES.put(14, 1); INDIRECT_COVERAGE_CHANGES.put(15, 1); for (int i = 1; i <= 25; i++) { - COVERAGE_PER_LINE.put(i, Coverage.NO_COVERAGE); + COVERAGE_PER_LINE.put(i, CoverageBuilder.NO_COVERAGE); } }