Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract a common base class for test parsers #70

Merged
merged 4 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/main/java/edu/hm/hafner/coverage/CoverageParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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. */
Expand All @@ -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

/**
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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<TestCase> readTestCases(final XMLEventReader eventReader,
final ModuleNode root) throws XMLStreamException {
String suiteName = EMPTY;
var tests = new ArrayList<TestCase>();
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();

Check warning on line 85 in src/main/java/edu/hm/hafner/coverage/parser/AbstractTestParser.java

View check run for this annotation

ci.jenkins.io / Mutation Coverage

Mutation survived

One mutation survived in line 85 (EmptyObjectReturnValsMutator)
Raw output
Survived mutations:
- replaced return value with "" for edu/hm/hafner/coverage/parser/AbstractTestParser::createId (org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator)
}
}
65 changes: 5 additions & 60 deletions src/main/java/edu/hm/hafner/coverage/parser/JunitParser.java
Original file line number Diff line number Diff line change
@@ -1,39 +1,27 @@
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.
*
* @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");

Expand All @@ -50,49 +38,16 @@
* @param processingMode
* 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<Object> 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()));

Check warning on line 50 in src/main/java/edu/hm/hafner/coverage/parser/JunitParser.java

View check run for this annotation

ci.jenkins.io / CPD

CPD

LOW: Found duplicated code.
Raw output
<pre><code>public JunitParser(final ProcessingMode processingMode) { super(processingMode, TEST_SUITE, TEST_CASE); } &#64;Override TestCase readTestCase(final XMLEventReader reader, final StartElement testCaseElement, final String suiteName, final ModuleNode root) throws XMLStreamException { var builder &#61; new TestCaseBuilder(); builder.withTestName(getOptionalValueOf(testCaseElement, NAME).orElse(createId()));</code></pre>

while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
Expand All @@ -117,13 +72,7 @@
}

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);
}
Expand Down Expand Up @@ -157,8 +106,4 @@
}
}
}

private String createId() {
return UUID.randomUUID().toString();
}
}
Loading