-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCoberturaCoverageParser.java
More file actions
62 lines (56 loc) · 2.67 KB
/
Copy pathCoberturaCoverageParser.java
File metadata and controls
62 lines (56 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package io.github.randomcodespace.sonarpredict.cli.coverage;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
/**
* Parses a Cobertura XML coverage report — the shape {@code coverage.py}'s
* {@code xml} formatter emits for Python projects.
*
* <p>Cobertura nests {@code <class filename="...">} under
* {@code <packages><package><classes>}; each class carries a {@code <lines>}
* block of {@code <line number="N" hits="H"/>} elements. Every {@code <line>}
* is a coverable line; a line is covered when {@code hits > 0}. Multiple
* {@code <class>} entries can share a {@code filename} (e.g. several classes in
* one source file), so their line counts are summed per file.
*/
public final class CoberturaCoverageParser implements CoverageParser {
@Override
public CoverageReport parse(Path path) {
Element root = CoverageXml.parse(path).getDocumentElement();
if (root == null || !"coverage".equals(root.getNodeName())) {
throw new CoverageException(
"not a Cobertura report: " + path + " (expected a <coverage> root)");
}
// Collect line numbers per filename: a source file may host several
// <class> entries (e.g. several classes in one Python module).
var coverableByFile = new java.util.LinkedHashMap<String,
java.util.NavigableSet<Integer>>();
var coveredByFile = new java.util.LinkedHashMap<String,
java.util.NavigableSet<Integer>>();
for (Element clazz : CoverageXml.elementsByTag(root, "class")) {
String filename = clazz.getAttribute("filename");
if (filename == null || filename.isBlank()) {
continue;
}
var coverable = coverableByFile.computeIfAbsent(
filename, k -> new java.util.TreeSet<>());
var covered = coveredByFile.computeIfAbsent(
filename, k -> new java.util.TreeSet<>());
for (Element lines : CoverageXml.children(clazz, "lines")) {
for (Element line : CoverageXml.children(lines, "line")) {
int number = CoverageXml.intAttr(line, "number");
coverable.add(number);
if (CoverageXml.intAttr(line, "hits") > 0) {
covered.add(number);
}
}
}
}
List<FileCoverage> files = new ArrayList<>();
coverableByFile.forEach((filename, coverable) ->
files.add(new FileCoverage(filename,
coveredByFile.get(filename), coverable)));
return new CoverageReport(files);
}
}