Index: tools/telemetry/third_party/coverage/coverage/xmlreport.py |
diff --git a/third_party/pycoverage/coverage/xmlreport.py b/tools/telemetry/third_party/coverage/coverage/xmlreport.py |
similarity index 61% |
copy from third_party/pycoverage/coverage/xmlreport.py |
copy to tools/telemetry/third_party/coverage/coverage/xmlreport.py |
index 26ac02ad13d06077a9e46da349c0b88ec13d919b..998b95999e2b0b2d2a6a8a0b1599c929e3f3fb5c 100644 |
--- a/third_party/pycoverage/coverage/xmlreport.py |
+++ b/tools/telemetry/third_party/coverage/coverage/xmlreport.py |
@@ -1,15 +1,29 @@ |
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt |
+ |
"""XML reporting for coverage.py""" |
-import os, sys, time |
+import os |
+import sys |
+import time |
import xml.dom.minidom |
-from coverage import __url__, __version__ |
-from coverage.backward import sorted, rpartition # pylint: disable=W0622 |
+from coverage import __url__, __version__, files |
from coverage.report import Reporter |
+DTD_URL = ( |
+ 'https://raw.githubusercontent.com/cobertura/web/' |
+ 'f0366e5e2cf18f111cbd61fc34ef720a6584ba02' |
+ '/htdocs/xml/coverage-03.dtd' |
+) |
+ |
+ |
def rate(hit, num): |
"""Return the fraction of `hit`/`num`, as a string.""" |
- return "%.4g" % (float(hit) / (num or 1.0)) |
+ if num == 0: |
+ return "1" |
+ else: |
+ return "%.4g" % (float(hit) / num) |
class XmlReporter(Reporter): |
@@ -18,14 +32,15 @@ class XmlReporter(Reporter): |
def __init__(self, coverage, config): |
super(XmlReporter, self).__init__(coverage, config) |
- self.packages = None |
+ self.source_paths = set() |
+ self.packages = {} |
self.xml_out = None |
- self.arcs = coverage.data.has_arcs() |
+ self.has_arcs = coverage.data.has_arcs() |
def report(self, morfs, outfile=None): |
"""Generate a Cobertura-compatible XML report for `morfs`. |
- `morfs` is a list of modules or filenames. |
+ `morfs` is a list of modules or file names. |
`outfile` is a file object to write the XML to. |
@@ -35,11 +50,7 @@ class XmlReporter(Reporter): |
# Create the DOM that will store the data. |
impl = xml.dom.minidom.getDOMImplementation() |
- docType = impl.createDocumentType( |
- "coverage", None, |
- "http://cobertura.sourceforge.net/xml/coverage-03.dtd" |
- ) |
- self.xml_out = impl.createDocument(None, "coverage", docType) |
+ self.xml_out = impl.createDocument(None, "coverage", None) |
# Write header stuff. |
xcoverage = self.xml_out.documentElement |
@@ -48,16 +59,27 @@ class XmlReporter(Reporter): |
xcoverage.appendChild(self.xml_out.createComment( |
" Generated by coverage.py: %s " % __url__ |
)) |
- xpackages = self.xml_out.createElement("packages") |
- xcoverage.appendChild(xpackages) |
+ xcoverage.appendChild(self.xml_out.createComment(" Based on %s " % DTD_URL)) |
# Call xml_file for each file in the data. |
- self.packages = {} |
self.report_files(self.xml_file, morfs) |
+ xsources = self.xml_out.createElement("sources") |
+ xcoverage.appendChild(xsources) |
+ |
+ # Populate the XML DOM with the source info. |
+ for path in sorted(self.source_paths): |
+ xsource = self.xml_out.createElement("source") |
+ xsources.appendChild(xsource) |
+ txt = self.xml_out.createTextNode(path) |
+ xsource.appendChild(txt) |
+ |
lnum_tot, lhits_tot = 0, 0 |
bnum_tot, bhits_tot = 0, 0 |
+ xpackages = self.xml_out.createElement("packages") |
+ xcoverage.appendChild(xpackages) |
+ |
# Populate the XML DOM with the package info. |
for pkg_name in sorted(self.packages.keys()): |
pkg_data = self.packages[pkg_name] |
@@ -70,7 +92,11 @@ class XmlReporter(Reporter): |
xclasses.appendChild(class_elts[class_name]) |
xpackage.setAttribute("name", pkg_name.replace(os.sep, '.')) |
xpackage.setAttribute("line-rate", rate(lhits, lnum)) |
- xpackage.setAttribute("branch-rate", rate(bhits, bnum)) |
+ if self.has_arcs: |
+ branch_rate = rate(bhits, bnum) |
+ else: |
+ branch_rate = "0" |
+ xpackage.setAttribute("branch-rate", branch_rate) |
xpackage.setAttribute("complexity", "0") |
lnum_tot += lnum |
@@ -79,7 +105,11 @@ class XmlReporter(Reporter): |
bhits_tot += bhits |
xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot)) |
- xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot)) |
+ if self.has_arcs: |
+ branch_rate = rate(bhits_tot, bnum_tot) |
+ else: |
+ branch_rate = "0" |
+ xcoverage.setAttribute("branch-rate", branch_rate) |
# Use the DOM to write the output file. |
outfile.write(self.xml_out.toprettyxml()) |
@@ -92,14 +122,20 @@ class XmlReporter(Reporter): |
pct = 100.0 * (lhits_tot + bhits_tot) / denom |
return pct |
- def xml_file(self, cu, analysis): |
+ def xml_file(self, fr, analysis): |
"""Add to the XML report for a single file.""" |
# Create the 'lines' and 'package' XML elements, which |
# are populated later. Note that a package == a directory. |
- package_name = rpartition(cu.name, ".")[0] |
- className = cu.name |
- |
+ filename = fr.relative_filename() |
+ filename = filename.replace("\\", "/") |
+ dirname = os.path.dirname(filename) or "." |
+ parts = dirname.split("/") |
+ dirname = "/".join(parts[:self.config.xml_package_depth]) |
+ package_name = dirname.replace("/", ".") |
+ className = fr.relative_filename() |
+ |
+ self.source_paths.add(files.relative_directory().rstrip('/')) |
package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) |
xclass = self.xml_out.createElement("class") |
@@ -109,12 +145,12 @@ class XmlReporter(Reporter): |
xlines = self.xml_out.createElement("lines") |
xclass.appendChild(xlines) |
- xclass.setAttribute("name", className) |
- filename = cu.file_locator.relative_filename(cu.filename) |
- xclass.setAttribute("filename", filename.replace("\\", "/")) |
+ xclass.setAttribute("name", os.path.relpath(filename, dirname)) |
+ xclass.setAttribute("filename", filename) |
xclass.setAttribute("complexity", "0") |
branch_stats = analysis.branch_stats() |
+ missing_branch_arcs = analysis.missing_branch_arcs() |
# For each statement, create an XML 'line' element. |
for line in sorted(analysis.statements): |
@@ -125,21 +161,25 @@ class XmlReporter(Reporter): |
# executed? If so, that should be recorded here. |
xline.setAttribute("hits", str(int(line not in analysis.missing))) |
- if self.arcs: |
+ if self.has_arcs: |
if line in branch_stats: |
total, taken = branch_stats[line] |
xline.setAttribute("branch", "true") |
- xline.setAttribute("condition-coverage", |
+ xline.setAttribute( |
+ "condition-coverage", |
"%d%% (%d/%d)" % (100*taken/total, taken, total) |
) |
+ if line in missing_branch_arcs: |
+ annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]] |
+ xline.setAttribute("missing-branches", ",".join(annlines)) |
xlines.appendChild(xline) |
class_lines = len(analysis.statements) |
class_hits = class_lines - len(analysis.missing) |
- if self.arcs: |
- class_branches = sum([t for t,k in branch_stats.values()]) |
- missing_branches = sum([t-k for t,k in branch_stats.values()]) |
+ if self.has_arcs: |
+ class_branches = sum(t for t, k in branch_stats.values()) |
+ missing_branches = sum(t - k for t, k in branch_stats.values()) |
class_br_hits = class_branches - missing_branches |
else: |
class_branches = 0.0 |
@@ -147,7 +187,12 @@ class XmlReporter(Reporter): |
# Finalize the statistics that are collected in the XML DOM. |
xclass.setAttribute("line-rate", rate(class_hits, class_lines)) |
- xclass.setAttribute("branch-rate", rate(class_br_hits, class_branches)) |
+ if self.has_arcs: |
+ branch_rate = rate(class_br_hits, class_branches) |
+ else: |
+ branch_rate = "0" |
+ xclass.setAttribute("branch-rate", branch_rate) |
+ |
package[0][className] = xclass |
package[1] += class_hits |
package[2] += class_lines |