-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
- 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 { | ||
|
||
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"); | ||
private static final QName CLASS_NAME = new QName("FullName"); | ||
private static final QName METHOD_NAME = new QName("Name"); | ||
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"); | ||
private static final QName METHOD_COMPLEXITY = new QName("cyclomaticComplexity"); | ||
|
||
@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.
|
||
|
||
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); | ||
} | ||
else if (FILE.equals(nextElement.getName())) { | ||
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 |
||
} | ||
|
||
} | ||
|
||
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.
|
||
|
||
PackageNode packageNode = null; | ||
Check notice Code scanning / CodeQL Unread local variable Note
Variable 'PackageNode packageNode' is never read.
|
||
while (reader.hasNext()) { | ||
XMLEvent event = reader.nextEvent(); | ||
if (event.isStartElement()) { | ||
var nextElement = event.asStartElement(); | ||
if (CLASS.equals(nextElement.getName())) { | ||
readClass(reader, nextElement, log); | ||
} | ||
else if (MODULE_NAME.equals(nextElement.getName())) { | ||
String packageName = reader.nextEvent().asCharacters().getData(); | ||
packageNode = root.findOrCreatePackageNode(packageName); | ||
} | ||
} | ||
} | ||
} | ||
|
||
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.
|
||
while (reader.hasNext()) { | ||
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.
|
||
} | ||
} | ||
} | ||
} | ||
|
||
private int readComplexity(final String c) { | ||
try { | ||
return Math.round(Float.parseFloat(c)); // some reports use float values | ||
} | ||
catch (NumberFormatException ignore) { | ||
return 0; | ||
} | ||
} | ||
|
||
private Node createClassNode(final FileNode file, final StartElement parentElement) { | ||
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'parentElement' is never used.
|
||
// Read summary has name | ||
return file.createClassNode(UUID.randomUUID().toString()); | ||
} | ||
|
||
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(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
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.ModuleNode; | ||
|
||
@DefaultLocale("en") | ||
class OpenCoverParserTest extends AbstractParserTest { | ||
|
||
@Override | ||
CoverageParser createParser() { | ||
return new OpenCoverParser(); | ||
} | ||
|
||
@Test | ||
void shouldReadReport() { | ||
readExampleReport(); | ||
} | ||
|
||
private ModuleNode readExampleReport() { | ||
return readReport("opencover.xml", new OpenCoverParser()); | ||
} | ||
|
||
@Override | ||
protected String getFolder() { | ||
return "opencover"; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<CoverageSession> | ||
<Summary numSequencePoints="15" visitedSequencePoints="9" numBranchPoints="6" visitedBranchPoints="3" sequenceCoverage="60" branchCoverage="50" maxCyclomaticComplexity="6" minCyclomaticComplexity="6" visitedClasses="1" numClasses="1" visitedMethods="1" numMethods="1" /> | ||
<Modules> | ||
<Module hash="31750BAB-054A-4C4B-8763-D76F95F0353A"> | ||
<ModulePath>ClassLibrary.dll</ModulePath> | ||
<ModuleTime>2019-12-13T01:36:13</ModuleTime> | ||
<ModuleName>ClassLibrary</ModuleName> | ||
<Files> | ||
<File uid="1" fullPath="[PLACEHOLDER]" /> | ||
</Files> | ||
<Classes> | ||
<Class> | ||
<Summary numSequencePoints="15" visitedSequencePoints="9" numBranchPoints="6" visitedBranchPoints="3" sequenceCoverage="60" branchCoverage="50" maxCyclomaticComplexity="6" minCyclomaticComplexity="6" visitedClasses="1" numClasses="1" visitedMethods="1" numMethods="1" /> | ||
<FullName>ClassLibrary.LibraryClass</FullName> | ||
<Methods> | ||
<Method cyclomaticComplexity="6" nPathComplexity="0" sequenceCoverage="60" branchCoverage="50" isConstructor="False" isGetter="False" isSetter="False" isStatic="True"> | ||
<Summary numSequencePoints="15" visitedSequencePoints="9" numBranchPoints="6" visitedBranchPoints="3" sequenceCoverage="60" branchCoverage="50" maxCyclomaticComplexity="6" minCyclomaticComplexity="6" visitedClasses="0" numClasses="0" visitedMethods="1" numMethods="1" /> | ||
<MetadataToken /> | ||
<Name>System.Int32 ClassLibrary.LibraryClass::Sum(System.Int32,System.Int32)</Name> | ||
<FileRef uid="1" /> | ||
<SequencePoints> | ||
<SequencePoint vc="2" uspid="8" ordinal="0" sl="8" sc="1" el="8" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="2" uspid="9" ordinal="1" sl="9" sc="1" el="9" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="2" uspid="10" ordinal="2" sl="10" sc="1" el="10" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="0" uspid="13" ordinal="3" sl="13" sc="1" el="13" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="0" uspid="14" ordinal="4" sl="14" sc="1" el="14" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="0" uspid="16" ordinal="5" sl="16" sc="1" el="16" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="0" uspid="17" ordinal="6" sl="17" sc="1" el="17" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="0" uspid="19" ordinal="7" sl="19" sc="1" el="19" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="0" uspid="20" ordinal="8" sl="20" sc="1" el="20" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="1" uspid="22" ordinal="9" sl="22" sc="1" el="22" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="1" uspid="23" ordinal="10" sl="23" sc="1" el="23" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="1" uspid="25" ordinal="11" sl="25" sc="1" el="25" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="1" uspid="26" ordinal="12" sl="26" sc="1" el="26" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="2" uspid="28" ordinal="13" sl="28" sc="1" el="28" ec="2" bec="0" bev="0" fileid="1" /> | ||
<SequencePoint vc="2" uspid="29" ordinal="14" sl="29" sc="1" el="29" ec="2" bec="0" bev="0" fileid="1" /> | ||
</SequencePoints> | ||
<BranchPoints> | ||
<BranchPoint vc="0" uspid="10" ordinal="1" path="1" offset="8" offsetend="35" sl="10" fileid="1" /> | ||
<BranchPoint vc="0" uspid="10" ordinal="2" path="2" offset="8" offsetend="44" sl="10" fileid="1" /> | ||
<BranchPoint vc="0" uspid="10" ordinal="3" path="3" offset="8" offsetend="53" sl="10" fileid="1" /> | ||
<BranchPoint vc="1" uspid="10" ordinal="4" path="4" offset="8" offsetend="62" sl="10" fileid="1" /> | ||
<BranchPoint vc="1" uspid="10" ordinal="5" path="5" offset="8" offsetend="71" sl="10" fileid="1" /> | ||
<BranchPoint vc="2" uspid="10" ordinal="0" path="0" offset="8" offsetend="80" sl="10" fileid="1" /> | ||
</BranchPoints> | ||
<MethodPoint vc="9" uspid="0" p8:type="SequencePoint" ordinal="0" offset="0" sc="0" sl="8" ec="1" el="29" bec="0" bev="0" fileid="1" xmlns:p8="xsi" /> | ||
</Method> | ||
</Methods> | ||
</Class> | ||
</Classes> | ||
</Module> | ||
</Modules> | ||
</CoverageSession> |