diff --git a/README.md b/README.md index 0785b961..1f4ba673 100644 --- a/README.md +++ b/README.md @@ -33,5 +33,6 @@ This library consists basically of two separate parts: * [JUnit](https://junit.org/junit5/) test results * [NUnit](https://nunit.org) test results * [XUnit](https://xunit.net) test results + * Metrics XML report All source code is licensed under the MIT license. Contributions to this library are welcome! diff --git a/src/main/java/edu/hm/hafner/coverage/CyclomaticComplexity.java b/src/main/java/edu/hm/hafner/coverage/CyclomaticComplexity.java index 5cc50a1a..9565b58b 100644 --- a/src/main/java/edu/hm/hafner/coverage/CyclomaticComplexity.java +++ b/src/main/java/edu/hm/hafner/coverage/CyclomaticComplexity.java @@ -32,7 +32,7 @@ public CyclomaticComplexity(final int complexity, final Metric metric) { } @Override - protected IntegerValue create(final int value) { - return new CyclomaticComplexity(value); + protected IntegerValue create(final int value, final Metric metric) { + return new CyclomaticComplexity(value, metric); } } diff --git a/src/main/java/edu/hm/hafner/coverage/IntegerValue.java b/src/main/java/edu/hm/hafner/coverage/IntegerValue.java index cd5b20a6..b2218ccf 100644 --- a/src/main/java/edu/hm/hafner/coverage/IntegerValue.java +++ b/src/main/java/edu/hm/hafner/coverage/IntegerValue.java @@ -40,10 +40,10 @@ public int getValue() { @Override public IntegerValue add(final Value other) { - return castAndMap(other, o -> create(integer + o.getValue())); + return castAndMap(other, o -> create(integer + o.getValue(), o.getMetric())); } - protected abstract IntegerValue create(int value); + protected abstract IntegerValue create(int value, Metric metric); @Override public IntegerValue max(final Value other) { diff --git a/src/main/java/edu/hm/hafner/coverage/LinesOfCode.java b/src/main/java/edu/hm/hafner/coverage/LinesOfCode.java index 6f4ec67e..d15bfa0c 100644 --- a/src/main/java/edu/hm/hafner/coverage/LinesOfCode.java +++ b/src/main/java/edu/hm/hafner/coverage/LinesOfCode.java @@ -19,7 +19,7 @@ public LinesOfCode(final int loc) { } @Override - protected IntegerValue create(final int value) { + protected IntegerValue create(final int value, final Metric ignored) { return new LinesOfCode(value); } } diff --git a/src/main/java/edu/hm/hafner/coverage/Metric.java b/src/main/java/edu/hm/hafner/coverage/Metric.java index a27f6a4f..f801b95e 100644 --- a/src/main/java/edu/hm/hafner/coverage/Metric.java +++ b/src/main/java/edu/hm/hafner/coverage/Metric.java @@ -41,7 +41,10 @@ public enum Metric { COMPLEXITY_MAXIMUM(new MethodMaxComplexityFinder(), MetricTendency.SMALLER_IS_BETTER), COMPLEXITY_DENSITY(new DensityEvaluator(), MetricTendency.SMALLER_IS_BETTER), LOC(new LocEvaluator(), MetricTendency.SMALLER_IS_BETTER), - TESTS(new ValuesAggregator(), MetricTendency.LARGER_IS_BETTER); + TESTS(new ValuesAggregator(), MetricTendency.LARGER_IS_BETTER), + NCSS(new ValuesAggregator(), MetricTendency.SMALLER_IS_BETTER), + COGNITIVE_COMPLEXITY(new ValuesAggregator(), MetricTendency.SMALLER_IS_BETTER), + NPATH_COMPLEXITY(new ValuesAggregator(), MetricTendency.SMALLER_IS_BETTER); /** * Returns the metric that belongs to the specified tag. diff --git a/src/main/java/edu/hm/hafner/coverage/TestCount.java b/src/main/java/edu/hm/hafner/coverage/TestCount.java index 33bfb188..d809416a 100644 --- a/src/main/java/edu/hm/hafner/coverage/TestCount.java +++ b/src/main/java/edu/hm/hafner/coverage/TestCount.java @@ -19,7 +19,7 @@ public TestCount(final int tests) { } @Override - protected IntegerValue create(final int value) { + protected IntegerValue create(final int value, final Metric ignored) { return new TestCount(value); } } diff --git a/src/main/java/edu/hm/hafner/coverage/Value.java b/src/main/java/edu/hm/hafner/coverage/Value.java index 239ad510..49f787e3 100644 --- a/src/main/java/edu/hm/hafner/coverage/Value.java +++ b/src/main/java/edu/hm/hafner/coverage/Value.java @@ -104,6 +104,12 @@ public static Value valueOf(final String stringRepresentation) { return new LinesOfCode(Integer.parseInt(value)); case TESTS: return new TestCount(Integer.parseInt(value)); + case NCSS: + return new CyclomaticComplexity(Integer.parseInt(value), Metric.NCSS); + case NPATH_COMPLEXITY: + return new CyclomaticComplexity(Integer.parseInt(value), Metric.NPATH_COMPLEXITY); + case COGNITIVE_COMPLEXITY: + return new CyclomaticComplexity(Integer.parseInt(value), Metric.COGNITIVE_COMPLEXITY); } } } diff --git a/src/main/java/edu/hm/hafner/coverage/parser/MetricsParser.java b/src/main/java/edu/hm/hafner/coverage/parser/MetricsParser.java new file mode 100644 index 00000000..c1423731 --- /dev/null +++ b/src/main/java/edu/hm/hafner/coverage/parser/MetricsParser.java @@ -0,0 +1,254 @@ +package edu.hm.hafner.coverage.parser; + +import java.io.Reader; +import java.nio.file.Paths; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; + +import edu.hm.hafner.coverage.ClassNode; +import edu.hm.hafner.coverage.CoverageParser; +import edu.hm.hafner.coverage.CyclomaticComplexity; +import edu.hm.hafner.coverage.FileNode; +import edu.hm.hafner.coverage.MethodNode; +import edu.hm.hafner.coverage.Metric; +import edu.hm.hafner.coverage.ModuleNode; +import edu.hm.hafner.coverage.Node; +import edu.hm.hafner.coverage.PackageNode; +import edu.hm.hafner.util.FilteredLog; +import edu.hm.hafner.util.PathUtil; +import edu.hm.hafner.util.SecureXmlParserFactory; +import edu.hm.hafner.util.TreeString; + +/** + * Parses Metrics reports into a hierarchical Java Object Model. + * + * @author Maximilian Waidelich + */ +@SuppressWarnings("PMD.GodClass") +public class MetricsParser extends CoverageParser { + private static final long serialVersionUID = -4461747681863455621L; + + /** XML elements. */ + private static final QName METRICS = new QName("metrics"); + private static final QName PACKAGE = new QName("package"); + private static final QName CLASS = new QName("class"); + private static final QName METHOD = new QName("method"); + private static final QName METRIC = new QName("metric"); + private static final QName FILE = new QName("file"); + + /** Attributes of the XML elements. */ + private static final QName PROJECT_NAME = new QName("projectName"); + private static final QName NAME = new QName("name"); + private static final QName BEGIN_LINE = new QName("beginline"); + private static final QName VALUE = new QName("value"); + + private static final String CYCLOMATIC_COMPLEXITY = "CyclomaticComplexity"; + private static final String COGNITIVE_COMPLEXITY = "CognitiveComplexity"; + private static final String NCSS = "NCSS"; + private static final String NPATH_COMPLEXITY = "NPathComplexity"; + + private static final PathUtil PATH_UTIL = new PathUtil(); + + /** + * Creates a new instance of {@link MetricsParser}. + */ + public MetricsParser() { + this(ProcessingMode.FAIL_FAST); + } + + /** + * Creates a new instance of {@link MetricsParser}. + * + * @param processingMode + * determines whether to ignore errors + */ + public MetricsParser(final ProcessingMode processingMode) { + super(processingMode); + } + + @Override + protected ModuleNode parseReport(final Reader reader, final String fileName, final FilteredLog log) { + try { + var factory = new SecureXmlParserFactory(); + var eventReader = factory.createXmlEventReader(reader); + + ModuleNode root = new ModuleNode(""); + + while (eventReader.hasNext()) { + XMLEvent event = eventReader.nextEvent(); + + if (event.isStartElement()) { + var startElement = event.asStartElement(); + var tagName = startElement.getName(); + if (METRICS.equals(tagName)) { + root = new ModuleNode(getOptionalValueOf(startElement, PROJECT_NAME).orElse("")); + } + else if (PACKAGE.equals(tagName)) { + readPackage(eventReader, root, startElement, fileName); + } + } + } + if (root.hasChildren()) { + return root; + } + else { + handleEmptyResults(fileName, log); + return new ModuleNode("empty"); + } + } + catch (XMLStreamException exception) { + throw new ParsingException(exception); + } + } + + @CanIgnoreReturnValue + private PackageNode readPackage(final XMLEventReader reader, final ModuleNode root, + final StartElement startElement, final String fileName) throws XMLStreamException { + var packageName = getValueOf(startElement, NAME); + var packageNode = root.findOrCreatePackageNode(packageName); + while (reader.hasNext()) { + XMLEvent event = reader.nextEvent(); + + if (event.isStartElement()) { + var nextElement = event.asStartElement(); + if (FILE.equals(nextElement.getName())) { + readSourceFile(reader, packageNode, nextElement, fileName); + } + else if (METRIC.equals(nextElement.getName())) { + readValueCounter(packageNode, nextElement); + } + } + else if (event.isEndElement()) { + var endElement = event.asEndElement(); + if (PACKAGE.equals(endElement.getName())) { + return packageNode; + } + } + } + throw createEofException(fileName); + } + + @CanIgnoreReturnValue + private Node readClass(final XMLEventReader reader, final FileNode fileNode, final StartElement startElement, + final String fileName, final PackageNode packageNode) throws XMLStreamException { + ClassNode classNode = fileNode.findOrCreateClassNode(packageNode.getName() + "." + getValueOf(startElement, NAME)); + while (reader.hasNext()) { + XMLEvent event = reader.nextEvent(); + + if (event.isStartElement()) { + var nextElement = event.asStartElement(); + if (METHOD.equals(nextElement.getName())) { + readMethod(reader, classNode, nextElement, fileName); + } + else if (METRIC.equals(nextElement.getName())) { + readValueCounter(classNode, nextElement); + } + } + else if (event.isEndElement()) { + var endElement = event.asEndElement(); + if (CLASS.equals(endElement.getName())) { + return classNode; + } + } + } + throw createEofException(fileName); + } + + private TreeString internPath(final String filePath) { + return getTreeStringBuilder().intern(PATH_UTIL.getRelativePath(Paths.get(filePath))); + } + + @CanIgnoreReturnValue + private Node readSourceFile(final XMLEventReader reader, final PackageNode packageNode, + final StartElement startElement, final String fileName) + throws XMLStreamException { + String sourceFileName = getSourceFileName(startElement); + var fileNode = packageNode.findOrCreateFileNode(sourceFileName, + internPath(getValueOf(startElement, NAME))); + + while (reader.hasNext()) { + XMLEvent event = reader.nextEvent(); + + if (event.isStartElement()) { + var nextElement = event.asStartElement(); + if (CLASS.equals(nextElement.getName())) { + readClass(reader, fileNode, nextElement, fileName, packageNode); + } + else if (METRIC.equals(nextElement.getName())) { + readValueCounter(fileNode, nextElement); + } + } + else if (event.isEndElement()) { + var endElement = event.asEndElement(); + if (FILE.equals(endElement.getName())) { + return fileNode; + } + } + } + throw createEofException(fileName); + } + + private String getSourceFileName(final StartElement startSourceFileElement) { + var sourceFilePath = Paths.get(getValueOf(startSourceFileElement, NAME)).getFileName(); + if (sourceFilePath == null) { + return getValueOf(startSourceFileElement, NAME); + } + else { + return sourceFilePath.toString(); + } + } + + @CanIgnoreReturnValue + private Node readMethod(final XMLEventReader reader, final ClassNode classNode, + final StartElement startElement, final String fileName) throws XMLStreamException { + String methodName = getValueOf(startElement, NAME) + "#" + getValueOf(startElement, BEGIN_LINE); + + MethodNode methodNode = createMethod(startElement, methodName); + classNode.addChild(methodNode); + + while (reader.hasNext()) { + XMLEvent event = reader.nextEvent(); + + if (event.isStartElement()) { + var nextElement = event.asStartElement(); + if (METRIC.equals(nextElement.getName())) { + readValueCounter(methodNode, nextElement); + } + } + else if (event.isEndElement()) { + var endElement = event.asEndElement(); + if (METHOD.equals(endElement.getName())) { + return methodNode; + } + } + } + throw createEofException(fileName); + } + + private MethodNode createMethod(final StartElement startElement, final String methodName) { + return new MethodNode(methodName, "", parseInteger(getValueOf(startElement, BEGIN_LINE))); + } + + private void readValueCounter(final Node node, final StartElement startElement) { + // FIXME: create Metric Values independent of Metric Name + String currentType = getValueOf(startElement, NAME); + int value = parseInteger(getValueOf(startElement, VALUE)); + if (CYCLOMATIC_COMPLEXITY.equals(currentType)) { + node.addValue(new CyclomaticComplexity(value)); + } + else if (COGNITIVE_COMPLEXITY.equals(currentType)) { + node.addValue(new CyclomaticComplexity(value, Metric.COGNITIVE_COMPLEXITY)); + } + else if (NCSS.equals(currentType)) { + node.addValue(new CyclomaticComplexity(value, Metric.NCSS)); + } + else if (NPATH_COMPLEXITY.equals(currentType)) { + node.addValue(new CyclomaticComplexity(value, Metric.NPATH_COMPLEXITY)); + } + } +} diff --git a/src/main/java/edu/hm/hafner/coverage/registry/ParserRegistry.java b/src/main/java/edu/hm/hafner/coverage/registry/ParserRegistry.java index 20c85d46..7b6db756 100644 --- a/src/main/java/edu/hm/hafner/coverage/registry/ParserRegistry.java +++ b/src/main/java/edu/hm/hafner/coverage/registry/ParserRegistry.java @@ -7,6 +7,7 @@ import edu.hm.hafner.coverage.parser.CoberturaParser; import edu.hm.hafner.coverage.parser.JacocoParser; import edu.hm.hafner.coverage.parser.JunitParser; +import edu.hm.hafner.coverage.parser.MetricsParser; import edu.hm.hafner.coverage.parser.NunitParser; import edu.hm.hafner.coverage.parser.OpenCoverParser; import edu.hm.hafner.coverage.parser.PitestParser; @@ -28,7 +29,8 @@ public enum CoverageParserType { PIT, JUNIT, VECTORCAST, - XUNIT + XUNIT, + METRICS } /** @@ -79,6 +81,8 @@ public CoverageParser get(final CoverageParserType parser, final ProcessingMode return new XunitParser(processingMode); case VECTORCAST: return new VectorCastParser(processingMode); + case METRICS: + return new MetricsParser(processingMode); } throw new IllegalArgumentException("Unknown parser type: " + parser); } diff --git a/src/test/java/edu/hm/hafner/coverage/IntegerValueTest.java b/src/test/java/edu/hm/hafner/coverage/IntegerValueTest.java index baab0e22..5ab1a77a 100644 --- a/src/test/java/edu/hm/hafner/coverage/IntegerValueTest.java +++ b/src/test/java/edu/hm/hafner/coverage/IntegerValueTest.java @@ -46,9 +46,9 @@ void shouldCreateValue() { var value = createValue(123); assertThat(value.serialize()).startsWith(value.getMetric().name()).endsWith(": 123"); - assertThat(value.create(100)).hasValue(100); - assertThat(value.create(-100)).hasValue(-100); - assertThat(value.create(0)).hasValue(0); + assertThat(value.create(100, Metric.COMPLEXITY)).hasValue(100); + assertThat(value.create(-100, Metric.COMPLEXITY)).hasValue(-100); + assertThat(value.create(0, Metric.COMPLEXITY)).hasValue(0); } @Test diff --git a/src/test/java/edu/hm/hafner/coverage/ValueTest.java b/src/test/java/edu/hm/hafner/coverage/ValueTest.java index 3f018191..2881b810 100644 --- a/src/test/java/edu/hm/hafner/coverage/ValueTest.java +++ b/src/test/java/edu/hm/hafner/coverage/ValueTest.java @@ -59,6 +59,12 @@ void shouldReturnCorrectValueOfIntegerValues() { .isInstanceOfSatisfying(LinesOfCode.class, value -> assertThat(value).hasValue(2)); assertThat(Value.valueOf("TESTS: 3")) .isInstanceOfSatisfying(TestCount.class, value -> assertThat(value).hasValue(3)); + assertThat(Value.valueOf("NCSS: 4")) + .isInstanceOfSatisfying(CyclomaticComplexity.class, value -> assertThat(value).hasValue(4)); + assertThat(Value.valueOf("NPATH_COMPLEXITY: 5")) + .isInstanceOfSatisfying(CyclomaticComplexity.class, value -> assertThat(value).hasValue(5)); + assertThat(Value.valueOf("COGNITIVE_COMPLEXITY: 6")) + .isInstanceOfSatisfying(CyclomaticComplexity.class, value -> assertThat(value).hasValue(6)); } @Test @@ -88,13 +94,22 @@ void shouldThrowExceptionOnInvalidStringRepresentation() { void shouldGetValue() { var linesOfCode = new LinesOfCode(10); var cyclomaticComplexity = new CyclomaticComplexity(20); + var ncss = new CyclomaticComplexity(30, Metric.NCSS); + var npathComplexity = new CyclomaticComplexity(40, Metric.NPATH_COMPLEXITY); + var coginitiveComplexity = new CyclomaticComplexity(50, Metric.COGNITIVE_COMPLEXITY); - List values = List.of(linesOfCode, cyclomaticComplexity); + List values = List.of(linesOfCode, cyclomaticComplexity, ncss, npathComplexity, coginitiveComplexity); assertThat(Value.getValue(Metric.LOC, values)) .isEqualTo(linesOfCode); assertThat(Value.getValue(Metric.COMPLEXITY, values)) .isEqualTo(cyclomaticComplexity); + assertThat(Value.getValue(Metric.NCSS, values)) + .isEqualTo(ncss); + assertThat(Value.getValue(Metric.NPATH_COMPLEXITY, values)) + .isEqualTo(npathComplexity); + assertThat(Value.getValue(Metric.COGNITIVE_COMPLEXITY, values)) + .isEqualTo(coginitiveComplexity); assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(() -> Value.getValue(Metric.LINE, values)) .withMessageContaining("No value for metric"); diff --git a/src/test/java/edu/hm/hafner/coverage/parser/MetricsParserTest.java b/src/test/java/edu/hm/hafner/coverage/parser/MetricsParserTest.java new file mode 100644 index 00000000..3a7737bc --- /dev/null +++ b/src/test/java/edu/hm/hafner/coverage/parser/MetricsParserTest.java @@ -0,0 +1,115 @@ +package edu.hm.hafner.coverage.parser; + +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.DefaultLocale; + +import edu.hm.hafner.coverage.CoverageParser; +import edu.hm.hafner.coverage.CoverageParser.ProcessingMode; +import edu.hm.hafner.coverage.IntegerValue; +import edu.hm.hafner.coverage.Metric; +import edu.hm.hafner.coverage.ModuleNode; +import edu.hm.hafner.coverage.Node; +import edu.hm.hafner.coverage.assertions.Assertions; + +import static edu.hm.hafner.coverage.Metric.*; +import static edu.hm.hafner.coverage.Metric.CLASS; +import static edu.hm.hafner.coverage.Metric.FILE; +import static edu.hm.hafner.coverage.assertions.Assertions.*; + +@DefaultLocale("en") +class MetricsParserTest extends AbstractParserTest { + @Override + CoverageParser createParser(final ProcessingMode processingMode) { + return new MetricsParser(processingMode); + } + + @Override + protected String getFolder() { + return "metrics"; + } + + @Test + void simpleMetrics() { + ModuleNode tree = readReport("metrics.xml"); + + assertThat(tree.getAll(MODULE)).hasSize(1); + assertThat(tree.getAll(PACKAGE)).hasSize(1); + assertThat(tree.getAll(FILE)).hasSize(2); + assertThat(tree.getAll(CLASS)).hasSize(3); + assertThat(tree.getAll(METHOD)).hasSize(6); + + assertThat(tree).hasOnlyMetrics(MODULE, PACKAGE, FILE, CLASS, METHOD, COMPLEXITY, COMPLEXITY_MAXIMUM, + NPATH_COMPLEXITY, NCSS, COGNITIVE_COMPLEXITY); + + assertThat(tree).hasName("testProject"); + + assertThat(tree.getChildren()).hasSize(1) + .element(0) + .satisfies(packageNode -> assertThat(packageNode).hasName("edu.hm.hafner.util")) + .satisfies(packageNode -> assertThat(packageNode).hasValueMetrics(NCSS)) + .satisfies(packageNode -> assertThat( + ((IntegerValue) packageNode.getValue(NCSS).get()).getValue()).isEqualTo(1000)) + .satisfies(packageNode -> assertThat(packageNode.getChildren()).hasSize(2)) + .satisfies(packageNode -> assertThat(packageNode.getChildren()).element(0) + .satisfies(fileNode -> assertThat(fileNode).hasName("Ensure.java")) + .satisfies(fileNode -> assertThat(fileNode).hasValueMetrics(NCSS)) + .satisfies(fileNode -> assertThat( + ((IntegerValue) fileNode.getValue(NCSS).get()).getValue()).isEqualTo(500)) + .satisfies(fileNode -> assertThat(fileNode.getChildren()).hasSize(2)) + .satisfies( + fileNode -> assertThat(fileNode.getChildren()).element(0).satisfies(this::checkEnsure)) + .satisfies(fileNode -> assertThat(fileNode.getChildren()).element(1) + .satisfies(this::checkIterableCondition))) + .satisfies(packageNode -> assertThat(packageNode.getChildren()).element(1) + .satisfies(fileNode -> assertThat(fileNode).hasName("FilteredLog.java")) + .satisfies(this::checkFilteredLog)); + } + + private void checkIterableCondition(final Node node) { + assertThat(node).satisfies( + classNode -> Assertions.assertThat(classNode).hasName("edu.hm.hafner.util.IterableCondition")) + .satisfies(classNode -> assertThat(classNode.getChildren()).hasSize(2)) + .satisfies(classNode -> assertThat(classNode.getChildren()).element(0) + .satisfies(methodNode -> Assertions.assertThat(methodNode).hasName("IterableCondition#205")) + .satisfies(methodNode -> Assertions.assertThat(methodNode).hasNoValueMetrics())) + .satisfies(classNode -> assertThat(classNode.getChildren()).element(1) + .satisfies(methodNode -> checkMethod(methodNode, "isNotEmpty#216", COGNITIVE_COMPLEXITY, 0))); + } + + private void checkEnsure(final Node node) { + assertThat(node).satisfies(classNode -> Assertions.assertThat(classNode).hasName("edu.hm.hafner.util.Ensure")) + .satisfies(classNode -> Assertions.assertThat(classNode).hasValueMetrics(NCSS)) + .satisfies( + classNode -> assertThat(((IntegerValue) classNode.getValue(NCSS).get()).getValue()).isEqualTo( + 149)) + .satisfies(classNode -> assertThat(classNode.getChildren()).hasSize(1) + .element(0) + .satisfies(methodNode -> checkMethod(methodNode, "that#47", COGNITIVE_COMPLEXITY, 0)) + .satisfies(methodNode -> checkMethod(methodNode, "that#47", COMPLEXITY, 1)) + .satisfies(methodNode -> checkMethod(methodNode, "that#47", NCSS, 2)) + .satisfies(methodNode -> checkMethod(methodNode, "that#47", NPATH_COMPLEXITY, 1))); + } + + private void checkFilteredLog(final Node fileNode) { + assertThat(fileNode.getChildren()).hasSize(1) + .element(0) + .satisfies(classNode -> Assertions.assertThat(classNode).hasName("edu.hm.hafner.util.FilteredLog")) + .satisfies(classNode -> assertThat(classNode).hasValueMetrics(NCSS)) + .satisfies( + classNode -> assertThat(((IntegerValue) classNode.getValue(NCSS).get()).getValue()).isEqualTo( + 96)) + .satisfies(classNode -> assertThat(classNode.getChildren()).hasSize(3)) + .satisfies(classNode -> assertThat(classNode.getChildren()).element(0) + .satisfies(methodNode -> checkMethod(methodNode, "FilteredLog#41", COMPLEXITY, 1))) + .satisfies(classNode -> assertThat(classNode.getChildren()).element(1) + .satisfies(methodNode -> checkMethod(methodNode, "FilteredLog#51", NCSS, 2))) + .satisfies(classNode -> assertThat(classNode.getChildren()).element(2) + .satisfies(methodNode -> checkMethod(methodNode, "FilteredLog#63", NPATH_COMPLEXITY, 1))); + } + + private void checkMethod(final Node methodNode, final String name, final Metric metric, final int expected) { + assertThat(methodNode).hasName(name); + assertThat(methodNode).hasValueMetrics(metric); + assertThat(((IntegerValue) methodNode.getValue(metric).get()).getValue()).isEqualTo(expected); + } +} diff --git a/src/test/java/edu/hm/hafner/coverage/registry/ParserRegistryTest.java b/src/test/java/edu/hm/hafner/coverage/registry/ParserRegistryTest.java index 6ff5ed76..11311690 100644 --- a/src/test/java/edu/hm/hafner/coverage/registry/ParserRegistryTest.java +++ b/src/test/java/edu/hm/hafner/coverage/registry/ParserRegistryTest.java @@ -4,6 +4,7 @@ import edu.hm.hafner.coverage.CoverageParser.ProcessingMode; import edu.hm.hafner.coverage.parser.CoberturaParser; +import edu.hm.hafner.coverage.parser.MetricsParser; import edu.hm.hafner.coverage.parser.VectorCastParser; import edu.hm.hafner.coverage.parser.JacocoParser; import edu.hm.hafner.coverage.parser.JunitParser; @@ -33,6 +34,7 @@ void shouldCreateSomeParsers() { assertThat(registry.get(CoverageParserType.XUNIT, ProcessingMode.IGNORE_ERRORS)).isInstanceOf(XunitParser.class); assertThat(registry.get(CoverageParserType.VECTORCAST, ProcessingMode.FAIL_FAST)) .isInstanceOf(VectorCastParser.class); + assertThat(registry.get(CoverageParserType.METRICS, ProcessingMode.IGNORE_ERRORS)).isInstanceOf(MetricsParser.class); } @Test diff --git a/src/test/resources/edu/hm/hafner/coverage/parser/metrics/empty.xml b/src/test/resources/edu/hm/hafner/coverage/parser/metrics/empty.xml new file mode 100644 index 00000000..fc7fe782 --- /dev/null +++ b/src/test/resources/edu/hm/hafner/coverage/parser/metrics/empty.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/test/resources/edu/hm/hafner/coverage/parser/metrics/metrics.xml b/src/test/resources/edu/hm/hafner/coverage/parser/metrics/metrics.xml new file mode 100644 index 00000000..4ef72063 --- /dev/null +++ b/src/test/resources/edu/hm/hafner/coverage/parser/metrics/metrics.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +