From aeb926ea126e424764342ca368c623bace6142c2 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 24 Jan 2024 12:10:51 +0100 Subject: [PATCH 1/3] Extract a common base class for test parsers. --- .../hm/hafner/coverage/CoverageParser.java | 22 +++- .../coverage/parser/AbstractTestParser.java | 87 +++++++++++++++ .../hafner/coverage/parser/JunitParser.java | 65 +---------- .../hafner/coverage/parser/NunitParser.java | 103 +++++------------- .../parser/opencover/opencover-with-bom.xml | 54 +++++++++ 5 files changed, 192 insertions(+), 139 deletions(-) create mode 100644 src/main/java/edu/hm/hafner/coverage/parser/AbstractTestParser.java create mode 100644 src/test/resources/edu/hm/hafner/coverage/parser/opencover/opencover-with-bom.xml diff --git a/src/main/java/edu/hm/hafner/coverage/CoverageParser.java b/src/main/java/edu/hm/hafner/coverage/CoverageParser.java index e3156f1f..7b20dd72 100644 --- a/src/main/java/edu/hm/hafner/coverage/CoverageParser.java +++ b/src/main/java/edu/hm/hafner/coverage/CoverageParser.java @@ -7,6 +7,7 @@ import javax.xml.namespace.QName; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; import edu.hm.hafner.util.FilteredLog; import edu.hm.hafner.util.SecureXmlParserFactory.ParsingException; @@ -18,6 +19,8 @@ * @author Ullrich Hafner */ public abstract class CoverageParser implements Serializable { + private static final long serialVersionUID = 3941742254762282096L; + /** Error message when there are no results. */ public static final String EMPTY_MESSAGE = "No data found in the specified file."; /** Toplevel module name. */ @@ -33,9 +36,7 @@ public enum ProcessingMode { FAIL_FAST } - private static final long serialVersionUID = 3941742254762282096L; private transient TreeStringBuilder treeStringBuilder = new TreeStringBuilder(); - private final ProcessingMode processingMode; // since 0.26.0 /** @@ -82,6 +83,23 @@ public ModuleNode parse(final Reader reader, final FilteredLog log) { return moduleNode; } + /** + * Returns the name of the specified element. + * + * @param event the event + * @return the name + */ + protected QName getElementName(final XMLEvent event) { + QName name; + if (event.isStartElement()) { + name = event.asStartElement().getName(); + } + else { + name = event.asEndElement().getName(); + } + return name; + } + /** * Handles processing of empty results. * diff --git a/src/main/java/edu/hm/hafner/coverage/parser/AbstractTestParser.java b/src/main/java/edu/hm/hafner/coverage/parser/AbstractTestParser.java new file mode 100644 index 00000000..c87aefdb --- /dev/null +++ b/src/main/java/edu/hm/hafner/coverage/parser/AbstractTestParser.java @@ -0,0 +1,87 @@ +package edu.hm.hafner.coverage.parser; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +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 edu.hm.hafner.coverage.CoverageParser; +import edu.hm.hafner.coverage.ModuleNode; +import edu.hm.hafner.coverage.TestCase; +import edu.hm.hafner.util.FilteredLog; +import edu.hm.hafner.util.SecureXmlParserFactory; +import edu.hm.hafner.util.SecureXmlParserFactory.ParsingException; + +/** + * Baseclass for test result parsers. + * + * @author Ullrich Hafner + */ +abstract class AbstractTestParser extends CoverageParser { + private static final long serialVersionUID = 3771784159977766871L; + + static final QName NAME = new QName("name"); + static final QName FAILURE = new QName("failure"); + static final QName MESSAGE = new QName("message"); + static final QName CLASS_NAME = new QName("classname"); + + private final QName testSuite; + private final QName testCase; + + AbstractTestParser(final ProcessingMode processingMode, final QName testSuite, final QName testCase) { + super(processingMode); + this.testSuite = testSuite; + this.testCase = testCase; + } + + QName getTestCase() { + return testCase; + } + + QName getTestSuite() { + return testSuite; + } + + @Override + protected ModuleNode parseReport(final Reader reader, final FilteredLog log) { + try { + var eventReader = new SecureXmlParserFactory().createXmlEventReader(reader); + var root = new ModuleNode(EMPTY); + var tests = readTestCases(eventReader, root); + handleEmptyResults(log, tests.isEmpty()); + return root; + } + catch (XMLStreamException exception) { + throw new ParsingException(exception); + } + } + + private List readTestCases(final XMLEventReader eventReader, + final ModuleNode root) throws XMLStreamException { + String suiteName = EMPTY; + var tests = new ArrayList(); + while (eventReader.hasNext()) { + XMLEvent event = eventReader.nextEvent(); + + if (event.isStartElement() && getTestSuite().equals(event.asStartElement().getName())) { + suiteName = getOptionalValueOf(event.asStartElement(), NAME).orElse(EMPTY); + } + else if (event.isStartElement() && getTestCase().equals(event.asStartElement().getName())) { + tests.add(readTestCase(eventReader, event.asStartElement(), suiteName, root)); + } + } + return tests; + } + + abstract TestCase readTestCase(XMLEventReader reader, StartElement testCaseElement, + String suiteName, ModuleNode root) throws XMLStreamException; + + protected String createId() { + return UUID.randomUUID().toString(); + } +} diff --git a/src/main/java/edu/hm/hafner/coverage/parser/JunitParser.java b/src/main/java/edu/hm/hafner/coverage/parser/JunitParser.java index b969c597..9782f704 100644 --- a/src/main/java/edu/hm/hafner/coverage/parser/JunitParser.java +++ b/src/main/java/edu/hm/hafner/coverage/parser/JunitParser.java @@ -1,22 +1,14 @@ package edu.hm.hafner.coverage.parser; -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; 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 edu.hm.hafner.coverage.CoverageParser; import edu.hm.hafner.coverage.ModuleNode; import edu.hm.hafner.coverage.TestCase; import edu.hm.hafner.coverage.TestCase.TestCaseBuilder; -import edu.hm.hafner.util.FilteredLog; -import edu.hm.hafner.util.SecureXmlParserFactory; -import edu.hm.hafner.util.SecureXmlParserFactory.ParsingException; /** * Parses reports in the JUnit format into a Java object model. @@ -24,16 +16,12 @@ * @author Ullrich Hafner */ @SuppressWarnings("checkstyle:ClassDataAbstractionCoupling") -public class JunitParser extends CoverageParser { +public class JunitParser extends AbstractTestParser { private static final long serialVersionUID = -5468593789018138107L; private static final QName TEST_SUITE = new QName("testsuite"); private static final QName TEST_CASE = new QName("testcase"); - private static final QName NAME = new QName("name"); - private static final QName CLASS_NAME = new QName("classname"); - private static final QName FAILURE = new QName("failure"); private static final QName FAILURE_TYPE = new QName("type"); - private static final QName MESSAGE = new QName("message"); private static final QName ERROR = new QName("error"); private static final QName SKIPPED = new QName("skipped"); @@ -51,45 +39,12 @@ public JunitParser() { * determines whether to ignore errors */ public JunitParser(final ProcessingMode processingMode) { - super(processingMode); + super(processingMode, TEST_SUITE, TEST_CASE); } @Override - protected ModuleNode parseReport(final Reader reader, final FilteredLog log) { - try { - var factory = new SecureXmlParserFactory(); - var eventReader = factory.createXmlEventReader(reader); - - var root = new ModuleNode(EMPTY); - var tests = readTestCases(eventReader, root); - handleEmptyResults(log, tests.isEmpty()); - return root; - } - catch (XMLStreamException exception) { - throw new ParsingException(exception); - } - } - - private List readTestCases(final XMLEventReader eventReader, - final ModuleNode root) throws XMLStreamException { - String suiteName = EMPTY; - var tests = new ArrayList<>(); - while (eventReader.hasNext()) { - XMLEvent event = eventReader.nextEvent(); - - if (event.isStartElement() && TEST_SUITE.equals(event.asStartElement().getName())) { - suiteName = getOptionalValueOf(event.asStartElement(), NAME).orElse(EMPTY); - } - else if (event.isStartElement() && TEST_CASE.equals(event.asStartElement().getName())) { - tests.add(readTestCase(eventReader, event.asStartElement(), suiteName, root)); - } - } - return tests; - } - - private TestCase readTestCase(final XMLEventReader reader, final StartElement testCaseElement, - final String suiteName, final ModuleNode root) - throws XMLStreamException { + TestCase readTestCase(final XMLEventReader reader, final StartElement testCaseElement, + final String suiteName, final ModuleNode root) throws XMLStreamException { var builder = new TestCaseBuilder(); builder.withTestName(getOptionalValueOf(testCaseElement, NAME).orElse(createId())); @@ -117,13 +72,7 @@ else if (event.isEndElement() && TEST_CASE.equals(event.asEndElement().getName() } private boolean isFailure(final XMLEvent event) { - QName name; - if (event.isStartElement()) { - name = event.asStartElement().getName(); - } - else { - name = event.asEndElement().getName(); - } + var name = getElementName(event); return FAILURE.equals(name) || ERROR.equals(name); } @@ -157,8 +106,4 @@ else if (event.isEndElement() && isFailure(event)) { } } } - - private String createId() { - return UUID.randomUUID().toString(); - } } diff --git a/src/main/java/edu/hm/hafner/coverage/parser/NunitParser.java b/src/main/java/edu/hm/hafner/coverage/parser/NunitParser.java index deb62848..5f7028c4 100644 --- a/src/main/java/edu/hm/hafner/coverage/parser/NunitParser.java +++ b/src/main/java/edu/hm/hafner/coverage/parser/NunitParser.java @@ -1,39 +1,28 @@ package edu.hm.hafner.coverage.parser; -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - 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 edu.hm.hafner.coverage.CoverageParser; import edu.hm.hafner.coverage.ModuleNode; import edu.hm.hafner.coverage.TestCase; import edu.hm.hafner.coverage.TestCase.TestCaseBuilder; -import edu.hm.hafner.util.FilteredLog; -import edu.hm.hafner.util.SecureXmlParserFactory; -import edu.hm.hafner.util.SecureXmlParserFactory.ParsingException; /** - * Parses reports in the NUnit format into a Java object model. + * Parses reports in the + * NUnit format + * into a Java object model. * * @author Valentin Delaye */ @SuppressWarnings("checkstyle:ClassDataAbstractionCoupling") -public class NunitParser extends CoverageParser { +public class NunitParser extends AbstractTestParser { private static final long serialVersionUID = -5468593789018138107L; private static final QName TEST_SUITE = new QName("test-suite"); private static final QName TEST_CASE = new QName("test-case"); - private static final QName NAME = new QName("name"); - private static final QName CLASS_NAME = new QName("classname"); - private static final QName FAILURE = new QName("failure"); - private static final QName MESSAGE = new QName("message"); private static final QName RESULT = new QName("result"); private static final String PASSED = "Passed"; private static final String FAILED = "Failed"; @@ -53,49 +42,37 @@ public NunitParser() { * determines whether to ignore errors */ public NunitParser(final ProcessingMode processingMode) { - super(processingMode); + super(processingMode, TEST_SUITE, TEST_CASE); } @Override - protected ModuleNode parseReport(final Reader reader, final FilteredLog log) { - try { - var factory = new SecureXmlParserFactory(); - var eventReader = factory.createXmlEventReader(reader); + TestCase readTestCase(final XMLEventReader reader, final StartElement testCaseElement, + final String suiteName, final ModuleNode root) throws XMLStreamException { + var builder = new TestCaseBuilder(); - var root = new ModuleNode(EMPTY); - var tests = readTestCases(eventReader, root); - handleEmptyResults(log, tests.isEmpty()); - return root; - } - catch (XMLStreamException exception) { - throw new ParsingException(exception); - } - } + builder.withTestName(getOptionalValueOf(testCaseElement, NAME).orElse(createId())); - private List readTestCases(final XMLEventReader eventReader, - final ModuleNode root) throws XMLStreamException { - String suiteName = EMPTY; - var tests = new ArrayList<>(); - while (eventReader.hasNext()) { - XMLEvent event = eventReader.nextEvent(); + readStatus(testCaseElement, builder); - if (event.isStartElement() && TEST_SUITE.equals(event.asStartElement().getName())) { - suiteName = getOptionalValueOf(event.asStartElement(), NAME).orElse(EMPTY); + while (reader.hasNext()) { + XMLEvent event = reader.nextEvent(); + + if (event.isStartElement() && isFailure(event)) { + readFailure(reader, builder); } - else if (event.isStartElement() && TEST_CASE.equals(event.asStartElement().getName())) { - tests.add(readTestCase(eventReader, event.asStartElement(), suiteName, root)); + else if (event.isEndElement() && TEST_CASE.equals(event.asEndElement().getName())) { + var className = getOptionalValueOf(testCaseElement, CLASS_NAME).orElse(suiteName); + builder.withClassName(className); + var packageNode = root.findOrCreatePackageNode(EMPTY); + var classNode = packageNode.findOrCreateClassNode(className); + classNode.addTestCase(builder.build()); + return builder.build(); } } - return tests; + throw createEofException(); } - private TestCase readTestCase(final XMLEventReader reader, final StartElement testCaseElement, - final String suiteName, final ModuleNode root) - throws XMLStreamException { - var builder = new TestCaseBuilder(); - - builder.withTestName(getOptionalValueOf(testCaseElement, NAME).orElse(createId())); - + private void readStatus(final StartElement testCaseElement, final TestCaseBuilder builder) { var status = getValueOf(testCaseElement, RESULT); switch (status) { case PASSED: @@ -109,40 +86,16 @@ private TestCase readTestCase(final XMLEventReader reader, final StartElement te builder.withStatus(TestCase.TestResult.SKIPPED); break; } - - while (reader.hasNext()) { - XMLEvent event = reader.nextEvent(); - - if (event.isStartElement() && isFailure(event)) { - readFailure(reader, builder); - } - else if (event.isEndElement() && TEST_CASE.equals(event.asEndElement().getName())) { - var className = getOptionalValueOf(testCaseElement, CLASS_NAME).orElse(suiteName); - builder.withClassName(className); - var packageNode = root.findOrCreatePackageNode(EMPTY); - var classNode = packageNode.findOrCreateClassNode(className); - classNode.addTestCase(builder.build()); - break; - } - } - return builder.build(); } private boolean isFailure(final XMLEvent event) { - QName name; - if (event.isStartElement()) { - name = event.asStartElement().getName(); - } - else { - name = event.asEndElement().getName(); - } - - return FAILURE.equals(name); + return FAILURE.equals(getElementName(event)); } private void readFailure(final XMLEventReader reader, final TestCaseBuilder builder) throws XMLStreamException { builder.withFailure(); + var aggregatedContent = new StringBuilder(); while (true) { XMLEvent event = reader.nextEvent(); @@ -158,8 +111,4 @@ else if (event.isEndElement() && event.asEndElement().getName().equals(MESSAGE)) } } } - - private String createId() { - return UUID.randomUUID().toString(); - } } diff --git a/src/test/resources/edu/hm/hafner/coverage/parser/opencover/opencover-with-bom.xml b/src/test/resources/edu/hm/hafner/coverage/parser/opencover/opencover-with-bom.xml new file mode 100644 index 00000000..908da9aa --- /dev/null +++ b/src/test/resources/edu/hm/hafner/coverage/parser/opencover/opencover-with-bom.xml @@ -0,0 +1,54 @@ + + + + + + ClassLibrary.dll + 2019-12-13T01:36:13 + ClassLibrary + + + + + + + ClassLibrary.LibraryClass + + + + + System.Int32 ClassLibrary.LibraryClass::Sum(System.Int32,System.Int32) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 338174c8f0953a8008dedb9e010d38b2a6c0c663 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 24 Jan 2024 12:12:30 +0100 Subject: [PATCH 2/3] Create a testcase for JENKINS-72595. --- pom.xml | 2 +- .../coverage/parser/OpenCoverParser.java | 11 +++++----- .../coverage/parser/AbstractParserTest.java | 8 +++++-- .../coverage/parser/OpenCoverParserTest.java | 21 ++++++++++++------- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 19f5c9d8..e713cbab 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ edu.hm.hafner codingstyle-pom - 3.40.0 + 3.41.0 diff --git a/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java b/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java index 7312c613..0aebdb98 100644 --- a/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java +++ b/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java @@ -37,8 +37,7 @@ */ @SuppressWarnings({"checkstyle:ClassDataAbstractionCoupling", "PMD.GodClass"}) public class OpenCoverParser extends CoverageParser { - private static final long serialVersionUID = 1L; - + private static final long serialVersionUID = -4819428317255612971L; private static final PathUtil PATH_UTIL = new PathUtil(); /** XML elements. */ @@ -62,8 +61,8 @@ public class OpenCoverParser extends CoverageParser { private static final QName METHOD_VISITED = new QName("visited"); private static final QName UID = new QName("uid"); private static final QName FULL_PATH = new QName("fullPath"); - private static final QName METHOD_INTRUCTION_COVERED = new QName("visitedSequencePoints"); - private static final QName METHOD_INTRUCTION_TOTAL = new QName("numSequencePoints"); + private static final QName METHOD_INSTRUCTION_COVERED = new QName("visitedSequencePoints"); + private static final QName METHOD_INSTRUCTION_TOTAL = new QName("numSequencePoints"); private static final QName METHOD_BRANCH_COVERED = new QName("visitedBranchPoints"); private static final QName METHOD_BRANCH_TOTAL = new QName("numBranchPoints"); private static final QName METHOD_CYCLOMATIC_COMPLEXITY = new QName("cyclomaticComplexity"); @@ -271,8 +270,8 @@ else if (event.isEndElement()) { private void readMethodSummary(final CoverageMethod coverageMethod, final StartElement startElement) { coverageMethod.setBranchCovered(getIntegerValueOf(startElement, METHOD_BRANCH_COVERED)); coverageMethod.setBranchMissed(getIntegerValueOf(startElement, METHOD_BRANCH_TOTAL) - coverageMethod.getBranchCovered()); - coverageMethod.setInstructionCovered(getIntegerValueOf(startElement, METHOD_INTRUCTION_COVERED)); - coverageMethod.setInstructionMissed(getIntegerValueOf(startElement, METHOD_INTRUCTION_TOTAL) - coverageMethod.getInstructionCovered()); + coverageMethod.setInstructionCovered(getIntegerValueOf(startElement, METHOD_INSTRUCTION_COVERED)); + coverageMethod.setInstructionMissed(getIntegerValueOf(startElement, METHOD_INSTRUCTION_TOTAL) - coverageMethod.getInstructionCovered()); } private void readSequencePoints(final XMLEventReader reader, final CoverageMethod coverageMethod) throws XMLStreamException { diff --git a/src/test/java/edu/hm/hafner/coverage/parser/AbstractParserTest.java b/src/test/java/edu/hm/hafner/coverage/parser/AbstractParserTest.java index 0c58fee1..0c4fa302 100644 --- a/src/test/java/edu/hm/hafner/coverage/parser/AbstractParserTest.java +++ b/src/test/java/edu/hm/hafner/coverage/parser/AbstractParserTest.java @@ -8,6 +8,7 @@ import java.util.NoSuchElementException; import java.util.Objects; +import org.apache.commons.io.input.BOMInputStream; import org.junit.jupiter.api.Test; import com.google.errorprone.annotations.MustBeClosed; @@ -51,7 +52,7 @@ ModuleNode readReport(final String fileName, final CoverageParser parser) { @MustBeClosed @SuppressFBWarnings("OBL") - private InputStream createFile(final String fileName) { + private InputStream createFile(final String fileName) throws IOException { String name; if (fileName.startsWith("/")) { name = fileName; @@ -59,7 +60,10 @@ private InputStream createFile(final String fileName) { else { name = getFolder() + "/" + fileName; } - return Objects.requireNonNull(AbstractParserTest.class.getResourceAsStream(name), "File not found: " + name); + var inputStream = Objects.requireNonNull(AbstractParserTest.class.getResourceAsStream(name), + "File not found: " + name); + + return BOMInputStream.builder().setInputStream(inputStream).get(); } protected abstract String getFolder(); diff --git a/src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java b/src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java index d30dbfcf..0da7fd45 100644 --- a/src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java +++ b/src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java @@ -1,7 +1,13 @@ package edu.hm.hafner.coverage.parser; +import java.util.List; +import java.util.stream.Collectors; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junitpioneer.jupiter.DefaultLocale; +import org.junitpioneer.jupiter.Issue; import edu.hm.hafner.coverage.Coverage; import edu.hm.hafner.coverage.Coverage.CoverageBuilder; @@ -14,11 +20,10 @@ import edu.hm.hafner.coverage.ModuleNode; import edu.hm.hafner.coverage.Node; +import static edu.hm.hafner.coverage.Metric.CLASS; +import static edu.hm.hafner.coverage.Metric.FILE; import static edu.hm.hafner.coverage.Metric.*; -import static edu.hm.hafner.coverage.assertions.Assertions.assertThat; - -import java.util.List; -import java.util.stream.Collectors; +import static edu.hm.hafner.coverage.assertions.Assertions.*; @DefaultLocale("en") class OpenCoverParserTest extends AbstractParserTest { @@ -103,9 +108,11 @@ void shouldDetectMethodCoverage() { coverage -> assertThat(coverage).hasTotal(21).hasCovered(19)); } - @Test - void shouldReportTestSourceFiles() { - ModuleNode module = readReport("opencover-reporttotestsourcefiles.xml"); + @ParameterizedTest + @Issue("JENKINS-72595") + @ValueSource(strings = {"opencover-reporttotestsourcefiles.xml", "opencover-with-bom.xml"}) + void shouldReportTestSourceFiles(final String fileName) { + ModuleNode module = readReport(fileName); assertThat(module.getAll(MODULE)).hasSize(2); assertThat(module.getAll(PACKAGE)).hasSize(1); assertThat(module.getAll(FILE)).hasSize(1); From b00d1b45bde8f857e3823bfcfa1e2a928e6b53a8 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 24 Jan 2024 12:18:49 +0100 Subject: [PATCH 3/3] Refactoring of open coverage parser. --- .../coverage/parser/OpenCoverParser.java | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java b/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java index 0aebdb98..512a9023 100644 --- a/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java +++ b/src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java @@ -3,6 +3,7 @@ import java.io.Reader; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -90,21 +91,19 @@ protected ModuleNode parseReport(final Reader reader, final FilteredLog log) { try { var eventReader = new SecureXmlParserFactory().createXmlEventReader(reader); var root = new ModuleNode(EMPTY); - var isEmpty = true; while (eventReader.hasNext()) { XMLEvent event = eventReader.nextEvent(); if (event.isStartElement()) { var startElement = event.asStartElement(); - var tagName = startElement.getName(); - if (MODULE.equals(tagName) && startElement.getAttributeByName(MODULE_SKIPPED) == null) { - isEmpty = readModule(eventReader, root); - if (!isEmpty) { + if (MODULE.equals(startElement.getName()) && startElement.getAttributeByName(MODULE_SKIPPED) == null) { + var hasModule = !readModule(eventReader, root); + if (hasModule) { return root; } } } } - handleEmptyResults(log, isEmpty); + handleEmptyResults(log); return new ModuleNode("empty"); } catch (XMLStreamException exception) { @@ -112,9 +111,8 @@ protected ModuleNode parseReport(final Reader reader, final FilteredLog log) { } } - @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CognitiveComplexity"}) private boolean readModule(final XMLEventReader reader, final ModuleNode root) throws XMLStreamException { - Map files = new LinkedHashMap<>(); + Map files = new HashMap<>(); List classes = new ArrayList<>(); boolean isEmpty = true; PackageNode packageNode = new PackageNode(EMPTY); @@ -151,21 +149,30 @@ else if (event.isEndElement()) { return true; } - // Creating all nodes + createNodes(files, packageNode, classes); + + return false; + } + + private void createNodes(final Map files, final PackageNode packageNode, + final List classes) { for (var file : files.entrySet()) { FileNode fileNode = packageNode.findOrCreateFileNode(getFileName(file.getValue()), getTreeStringBuilder().intern(file.getValue())); for (CoverageClassHolder clazz : classes) { if (clazz.hasMethods() && clazz.getFileId() != null && clazz.getFileId().equals(file.getKey())) { - ClassNode classNode = fileNode.createClassNode(clazz.getClassName()); - for (var method : clazz.getMethods()) { - if (classNode.findMethod(method.getMethodName(), method.getMethodName()).isEmpty()) { - createPoints(fileNode, classNode, method); - } - } + createClassWithMethods(clazz, fileNode); } } } - return false; + } + + private void createClassWithMethods(final CoverageClassHolder clazz, final FileNode fileNode) { + ClassNode classNode = fileNode.createClassNode(clazz.getClassName()); + for (var method : clazz.getMethods()) { + if (classNode.findMethod(method.getMethodName(), method.getMethodName()).isEmpty()) { + createPoints(fileNode, classNode, method); + } + } } private void createPoints(final FileNode fileNode, final ClassNode classNode, final CoverageMethod method) {