-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
Signed-off-by: Valentin Delaye <jonesbusy@gmail.com>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package edu.hm.hafner.coverage.parser; | ||
|
||
import java.io.Reader; | ||
import java.nio.file.Paths; | ||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
import java.util.NoSuchElementException; | ||
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.FileNode; | ||
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.SecureXmlParserFactory.ParsingException; | ||
|
||
/** | ||
* A parser which parses reports made by OpenCover into a Java Object Model. | ||
* | ||
*/ | ||
public class OpenCoverParser extends CoverageParser { | ||
Check warning on line 30 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CheckStyleRegexpMultilineCheck
Raw output
|
||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
private static final PathUtil PATH_UTIL = new PathUtil(); | ||
|
||
/** XML elements. */ | ||
private static final QName MODULE = new QName("Module"); | ||
private static final QName CLASS = new QName("Class"); | ||
private static final QName METHOD = new QName("Method"); | ||
Check warning on line 39 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedPrivateField
Raw output
|
||
private static final QName CLASS_NAME = new QName("FullName"); | ||
private static final QName METHOD_NAME = new QName("Name"); | ||
Check warning on line 41 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedPrivateField
Raw output
|
||
private static final QName MODULE_NAME = new QName("ModuleName"); | ||
private static final QName FILE = new QName("File"); | ||
|
||
private static final QName UID = new QName("uid"); | ||
private static final QName FULL_PATH = new QName("fullPath"); | ||
private static final QName CLASS_COMPLEXITY = new QName("maxCyclomaticComplexity"); | ||
Check warning on line 47 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedPrivateField
Raw output
|
||
private static final QName METHOD_COMPLEXITY = new QName("cyclomaticComplexity"); | ||
Check warning on line 48 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedPrivateField
Raw output
|
||
|
||
@Override | ||
protected ModuleNode parseReport(final Reader reader, final FilteredLog log) { | ||
try { | ||
var eventReader = new SecureXmlParserFactory().createXmlEventReader(reader); | ||
var root = new ModuleNode("-"); | ||
boolean isEmpty = true; | ||
while (eventReader.hasNext()) { | ||
XMLEvent event = eventReader.nextEvent(); | ||
if (event.isStartElement()) { | ||
var startElement = event.asStartElement(); | ||
var tagName = startElement.getName(); | ||
if (MODULE.equals(tagName)) { | ||
readPackage(eventReader, root, startElement, log); | ||
isEmpty = false; | ||
} | ||
} | ||
} | ||
if (isEmpty) { | ||
throw new NoSuchElementException("No coverage information found in the specified file."); | ||
} | ||
return root; | ||
} | ||
catch (XMLStreamException exception) { | ||
throw new ParsingException(exception); | ||
} | ||
} | ||
|
||
private void readPackage(final XMLEventReader reader, final ModuleNode root, | ||
final StartElement currentStartElement, final FilteredLog log) throws XMLStreamException { | ||
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'currentStartElement' is never used.
Check warning on line 78 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CheckStyleRegexpMultilineCheck
Raw output
Check warning on line 78 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedFormalParameter
Raw output
|
||
|
||
Map<String, String> files = new LinkedHashMap<>(); | ||
PackageNode packageNode = null; | ||
while (reader.hasNext()) { | ||
XMLEvent event = reader.nextEvent(); | ||
if (event.isStartElement()) { | ||
var nextElement = event.asStartElement(); | ||
if (CLASS.equals(nextElement.getName())) { | ||
readClass(reader, nextElement, log); | ||
Check warning on line 87 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / Mutation CoverageMutation survived
Raw output
|
||
} | ||
Check warning on line 88 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CPDCPD
Raw output
|
||
else if (FILE.equals(nextElement.getName())) { | ||
Check warning on line 89 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CPDCPD
Raw output
|
||
var fileName = getValueOf(nextElement, FULL_PATH); | ||
var uid = getValueOf(nextElement, UID); | ||
var relativePath = PATH_UTIL.getRelativePath(fileName); | ||
files.put(uid, relativePath); | ||
} | ||
else if (MODULE_NAME.equals(nextElement.getName())) { | ||
String packageName = reader.nextEvent().asCharacters().getData(); | ||
packageNode = root.findOrCreatePackageNode(packageName); | ||
} | ||
} | ||
} | ||
|
||
// Creating all file nodes | ||
for (var entry : files.entrySet()) { | ||
packageNode.findOrCreateFileNode(getFileName(entry.getValue()), getTreeStringBuilder().intern(entry.getValue())); | ||
Check warning Code scanning / CodeQL Dereferenced variable may be null Warning
Variable
packageNode Error loading related location Loading this Error loading related location Loading |
||
} | ||
Check warning on line 105 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CheckStyleRegexpMultilineCheck
Raw output
|
||
|
||
} | ||
|
||
private void readFile(final XMLEventReader reader, final ModuleNode root, | ||
final StartElement currentStartElement, final FilteredLog log) throws XMLStreamException { | ||
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'currentStartElement' is never used.
Check warning on line 110 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CheckStyleRegexpMultilineCheck
Raw output
Check warning on line 110 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedFormalParameter
Raw output
|
||
|
||
PackageNode packageNode = null; | ||
Check notice Code scanning / CodeQL Unread local variable Note
Variable 'PackageNode packageNode' is never read.
Check warning on line 112 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedLocalVariable
Raw output
Check warning on line 112 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedAssignment
Raw output
|
||
while (reader.hasNext()) { | ||
XMLEvent event = reader.nextEvent(); | ||
if (event.isStartElement()) { | ||
var nextElement = event.asStartElement(); | ||
if (CLASS.equals(nextElement.getName())) { | ||
readClass(reader, nextElement, log); | ||
} | ||
Check warning on line 119 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CPDCPD
Raw output
|
||
else if (MODULE_NAME.equals(nextElement.getName())) { | ||
Check warning on line 120 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CPDCPD
Raw output
|
||
String packageName = reader.nextEvent().asCharacters().getData(); | ||
packageNode = root.findOrCreatePackageNode(packageName); | ||
Check warning on line 122 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / SpotBugsDLS_DEAD_LOCAL_STORE
Raw output
Check warning on line 122 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedAssignment
Raw output
|
||
} | ||
} | ||
} | ||
} | ||
Check warning on line 126 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / Code CoverageNot covered lines
Check warning on line 126 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / SpotBugsUPM_UNCALLED_PRIVATE_METHOD
Raw output
|
||
|
||
private void readClass(final XMLEventReader reader, final StartElement parentElement, final FilteredLog log) throws XMLStreamException { | ||
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'parentElement' is never used.
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'log' is never used.
Check warning on line 128 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedFormalParameter
Raw output
Check warning on line 128 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedFormalParameter
Raw output
|
||
while (reader.hasNext()) { | ||
Check warning on line 129 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / Mutation CoverageMutation survived
Raw output
|
||
XMLEvent event = reader.nextEvent(); | ||
if (event.isStartElement()) { | ||
var nextElement = event.asStartElement(); | ||
if (CLASS_NAME.equals(nextElement.getName())) { | ||
String className = reader.nextEvent().asCharacters().getData(); | ||
Check notice Code scanning / CodeQL Unread local variable Note
Variable 'String className' is never read.
Check warning on line 134 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / SpotBugsDLS_DEAD_LOCAL_STORE
Raw output
Check warning on line 134 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedLocalVariable
Raw output
|
||
} | ||
} | ||
} | ||
} | ||
|
||
private int readComplexity(final String c) { | ||
try { | ||
return Math.round(Float.parseFloat(c)); // some reports use float values | ||
} | ||
catch (NumberFormatException ignore) { | ||
return 0; | ||
Check warning on line 145 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / SpotBugsUPM_UNCALLED_PRIVATE_METHOD
Raw output
|
||
} | ||
} | ||
|
||
private Node createClassNode(final FileNode file, final StartElement parentElement) { | ||
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'parentElement' is never used.
Check warning on line 149 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / PMDUnusedFormalParameter
Raw output
|
||
// Read summary has name | ||
return file.createClassNode(UUID.randomUUID().toString()); | ||
Check warning on line 151 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / Code CoverageNot covered lines
Check warning on line 151 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / SpotBugsUPM_UNCALLED_PRIVATE_METHOD
Raw output
|
||
} | ||
|
||
protected static String getValueOf(final StartElement element, final QName attribute) { | ||
return getOptionalValueOf(element, attribute).orElseThrow( | ||
() -> new NoSuchElementException(String.format( | ||
"Could not obtain attribute '%s' from element '%s'", attribute, element))); | ||
} | ||
|
||
private String getFileName(final String relativePath) { | ||
var path = Paths.get(PATH_UTIL.getAbsolutePath(relativePath)).getFileName(); | ||
if (path == null) { | ||
return relativePath; | ||
} | ||
return path.toString(); | ||
} | ||
Check warning on line 166 in src/main/java/edu/hm/hafner/coverage/parser/OpenCoverParser.java ci.jenkins.io / CheckStyleRegexpMultilineCheck
Raw output
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package edu.hm.hafner.coverage.parser; | ||
|
||
|
||
import org.apache.commons.lang3.StringUtils; | ||
Check warning on line 4 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / CheckStyleEmptyLineSeparatorCheck
Raw output
Check warning on line 4 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / PMDUnnecessaryImport
Raw output
|
||
import org.junit.jupiter.api.Test; | ||
import org.junitpioneer.jupiter.DefaultLocale; | ||
|
||
import edu.hm.hafner.coverage.ClassNode; | ||
import edu.hm.hafner.coverage.Coverage; | ||
import edu.hm.hafner.coverage.Coverage.CoverageBuilder; | ||
Check warning on line 10 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / PMDUnnecessaryImport
Raw output
|
||
import edu.hm.hafner.coverage.CoverageParser; | ||
import edu.hm.hafner.coverage.CyclomaticComplexity; | ||
Check warning on line 12 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / PMDUnnecessaryImport
Raw output
|
||
import edu.hm.hafner.coverage.FileNode; | ||
import edu.hm.hafner.coverage.LinesOfCode; | ||
Check warning on line 14 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / PMDUnnecessaryImport
Raw output
|
||
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.coverage.Percentage; | ||
Check warning on line 19 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / PMDUnnecessaryImport
Raw output
|
||
|
||
import static edu.hm.hafner.coverage.Metric.CLASS; | ||
Check warning on line 21 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / PMDUnnecessaryImport
Raw output
|
||
import static edu.hm.hafner.coverage.Metric.FILE; | ||
import static edu.hm.hafner.coverage.Metric.*; | ||
import static edu.hm.hafner.coverage.assertions.Assertions.*; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
Check warning on line 27 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / PMDUnnecessaryImport
Raw output
|
||
|
||
|
||
@DefaultLocale("en") | ||
Check warning on line 30 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / CheckStyleEmptyLineSeparatorCheck
Raw output
|
||
class OpenCoverParserTest extends AbstractParserTest { | ||
Check warning on line 31 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / CheckStyleRegexpMultilineCheck
Raw output
|
||
|
||
private static final String PROJECT_NAME = "Java coding style"; | ||
private static final String EMPTY = "-"; | ||
|
||
@Override | ||
CoverageParser createParser() { | ||
return new OpenCoverParser(); | ||
} | ||
|
||
@Test | ||
void shouldReadReport() { | ||
readExampleReport(); | ||
} | ||
|
||
@Test | ||
void shouldCreatePackageName() { | ||
ModuleNode tree = readExampleReport(); | ||
|
||
String fileName = "MyLogging.FancyClass.cs"; | ||
assertThat(tree.find(FILE, fileName)).isNotEmpty() | ||
.hasValueSatisfying(node -> assertThat(node).hasName(fileName) | ||
.hasParentName("MyLogging") | ||
.hasParent() | ||
.isNotRoot()); | ||
|
||
tree.splitPackages(); | ||
assertThat(tree.find(FILE, fileName)).isNotEmpty() | ||
.hasValueSatisfying(node -> assertThat(node).hasName(fileName) | ||
.hasParentName("MyLogging") | ||
.hasParent() | ||
.isNotRoot()); | ||
} | ||
|
||
@Test | ||
void shouldSplitPackages() { | ||
ModuleNode tree = readExampleReport(); | ||
|
||
tree.splitPackages(); | ||
|
||
verifyCoverageMetrics(tree); | ||
|
||
assertThat(tree.getAll(PACKAGE)).hasSize(1); | ||
|
||
// TODO: Assertions | ||
} | ||
|
||
@Test | ||
void shouldFilterByFiles() { | ||
var root = readExampleReport(); | ||
|
||
assertThat(root.getAll(MODULE)).hasSize(1); | ||
Check warning on line 82 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / CPDCPD
Raw output
|
||
assertThat(root.getAll(PACKAGE)).hasSize(1); | ||
assertThat(root.getAll(FILE)).hasSize(3); | ||
|
||
// TODO: Not yet implemented on parser | ||
//assertThat(root.getAll(CLASS)).hasSize(18); | ||
//assertThat(root.getAll(METHOD)).hasSize(102); | ||
|
||
// TODO: Assertions | ||
} | ||
|
||
private void verifyCoverageMetrics(final Node tree) { | ||
List<Node> nodes = tree.getAll(FILE); | ||
|
||
long missedInstructions = 0; | ||
long coveredInstructions = 0; | ||
long missedBranches = 0; | ||
long coveredBranches = 0; | ||
long missedLines = 0; | ||
long coveredLines = 0; | ||
for (Node node : nodes) { | ||
var instructionCoverage = (Coverage) node.getValue(INSTRUCTION).orElse(Coverage.nullObject(INSTRUCTION)); | ||
missedInstructions = missedInstructions + instructionCoverage.getMissed(); | ||
coveredInstructions = coveredInstructions + instructionCoverage.getCovered(); | ||
var branchCoverage = (Coverage) node.getValue(BRANCH).orElse(Coverage.nullObject(BRANCH)); | ||
Check warning on line 106 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / CPDCPD
Raw output
|
||
missedBranches = missedBranches + branchCoverage.getMissed(); | ||
coveredBranches = coveredBranches + branchCoverage.getCovered(); | ||
var lineCoverage = (Coverage) node.getValue(LINE).orElse(Coverage.nullObject(LINE)); | ||
missedLines = missedLines + lineCoverage.getMissed(); | ||
coveredLines = coveredLines + lineCoverage.getCovered(); | ||
} | ||
|
||
// TODO: Assertions | ||
} | ||
|
||
private PackageNode getPackage(final Node node) { | ||
var children = node.getChildren(); | ||
assertThat(children).hasSize(1).first().isInstanceOf(PackageNode.class); | ||
|
||
return (PackageNode) children.get(0); | ||
} | ||
|
||
private ClassNode getFirstClass(final Node node) { | ||
var packageNode = getPackage(node); | ||
|
||
var children = packageNode.getChildren(); | ||
assertThat(children).isNotEmpty().first().isInstanceOf(ClassNode.class); | ||
|
||
return (ClassNode) children.get(0); | ||
Check warning on line 130 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / SpotBugsUPM_UNCALLED_PRIVATE_METHOD
Raw output
|
||
} | ||
|
||
private FileNode getFileNode(final ModuleNode a) { | ||
Check warning on line 133 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / CPDCPD
Raw output
|
||
var fileNodes = a.getAllFileNodes(); | ||
assertThat(fileNodes).hasSize(1); | ||
|
||
var lineRange = fileNodes.get(0); | ||
assertThat(lineRange) | ||
.hasName("MyLogging.FancyClass.cs") | ||
.hasRelativePath("opencovertests\\MyLogging.FancyClass.cs"); | ||
|
||
return lineRange; | ||
Check warning on line 142 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / SpotBugsUPM_UNCALLED_PRIVATE_METHOD
Raw output
|
||
} | ||
|
||
private static Coverage getCoverage(final Node node, final Metric metric) { | ||
return (Coverage) node.getValue(metric).get(); | ||
Check warning on line 146 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / SpotBugsUPM_UNCALLED_PRIVATE_METHOD
Raw output
|
||
} | ||
|
||
private ModuleNode readExampleReport() { | ||
return readReport("opencover.xml", new OpenCoverParser()); | ||
} | ||
|
||
@Override | ||
protected String getFolder() { | ||
return "opencover"; | ||
} | ||
Check warning on line 156 in src/test/java/edu/hm/hafner/coverage/parser/OpenCoverParserTest.java ci.jenkins.io / CheckStyleRegexpMultilineCheck
Raw output
|
||
|
||
} |