diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2015-01-19 18:03:21 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2015-01-19 18:03:21 -0500 |
commit | e03bfa80f990b0f30eedf4da3aa7cedfcb2ead27 (patch) | |
tree | 3aead9fa06c73eed9479e4a15099416a262ba740 | |
parent | ddf3884d15ac50d4cff3b15742bcb804588e4006 (diff) | |
download | python-coveragepy-git-e03bfa80f990b0f30eedf4da3aa7cedfcb2ead27.tar.gz |
Fix bad regression: XML report now reports packages again. #235
-rw-r--r-- | CHANGES.txt | 5 | ||||
-rw-r--r-- | coverage/xmlreport.py | 19 | ||||
-rw-r--r-- | tests/farm/html/gold_x_xml/coverage.xml | 2 | ||||
-rw-r--r-- | tests/farm/html/gold_y_xml_branch/coverage.xml | 2 | ||||
-rw-r--r-- | tests/test_xml.py | 71 |
5 files changed, 90 insertions, 9 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 39650a1f..7bd3bd60 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,10 @@ Latest describe a/b/c.py as "a/b/c". Now it is shown as "a/b/c.py". This allows for better support of non-Python files, and also fixed `issue 69`_. +- The XML report now reports each directory as a package again. This was a bad + regression, I apologize. This was reported in `issue 235`_, which is now + fixed. + - When looking for the source for a frame, check if the file exists. On Windows, .pyw files are no longer recorded as .py files. Along the way, this fixed `issue 290`_. @@ -21,6 +25,7 @@ Latest to provide error messages earlier (`issue 349`_). .. _issue 69: https://bitbucket.org/ned/coveragepy/issue/69/coverage-html-overwrite-files-that-doesnt +.. _issue 235: https://bitbucket.org/ned/coveragepy/issue/235/package-name-is-missing-in-xml-report .. _issue 290: https://bitbucket.org/ned/coveragepy/issue/290/running-programmatically-with-pyw-files .. _issue 345: https://bitbucket.org/ned/coveragepy/issue/345/xml-reports-line-rate-0-for-empty-files .. _issue 349: https://bitbucket.org/ned/coveragepy/issue/349/bad-regex-in-config-should-get-an-earlier diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py index 45a4652e..3510c5b6 100644 --- a/coverage/xmlreport.py +++ b/coverage/xmlreport.py @@ -1,11 +1,14 @@ """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.report import Reporter + def rate(hit, num): """Return the fraction of `hit`/`num`, as a string.""" if num == 0: @@ -118,7 +121,10 @@ class XmlReporter(Reporter): # Create the 'lines' and 'package' XML elements, which # are populated later. Note that a package == a directory. - package_name = cu.name.rpartition(".")[0] + filename = cu.file_locator.relative_filename(cu.filename) + filename = filename.replace("\\", "/") + dirname = os.path.dirname(filename) or "." + package_name = dirname.replace("/", ".") className = cu.name self.source_paths.add(cu.file_locator.relative_dir.rstrip('/')) @@ -131,9 +137,8 @@ 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() @@ -151,7 +156,8 @@ class XmlReporter(Reporter): 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) ) xlines.appendChild(xline) @@ -174,6 +180,7 @@ class XmlReporter(Reporter): else: branch_rate = "0" xclass.setAttribute("branch-rate", branch_rate) + package[0][className] = xclass package[1] += class_hits package[2] += class_lines diff --git a/tests/farm/html/gold_x_xml/coverage.xml b/tests/farm/html/gold_x_xml/coverage.xml index f528e123..108cc528 100644 --- a/tests/farm/html/gold_x_xml/coverage.xml +++ b/tests/farm/html/gold_x_xml/coverage.xml @@ -7,7 +7,7 @@ <source></source>
</sources>
<packages>
- <package branch-rate="0" complexity="0" line-rate="0.6667" name="a">
+ <package branch-rate="0" complexity="0" line-rate="0.6667" name=".">
<classes>
<class branch-rate="0" complexity="0" filename="a.py" line-rate="0.6667" name="a.py">
<methods/>
diff --git a/tests/farm/html/gold_y_xml_branch/coverage.xml b/tests/farm/html/gold_y_xml_branch/coverage.xml index eb0c5323..8ac186b5 100644 --- a/tests/farm/html/gold_y_xml_branch/coverage.xml +++ b/tests/farm/html/gold_y_xml_branch/coverage.xml @@ -7,7 +7,7 @@ <source></source>
</sources>
<packages>
- <package branch-rate="0.5" complexity="0" line-rate="0.8" name="y">
+ <package branch-rate="0.5" complexity="0" line-rate="0.8" name=".">
<classes>
<class branch-rate="0.5" complexity="0" filename="y.py" line-rate="0.8" name="y.py">
<methods/>
diff --git a/tests/test_xml.py b/tests/test_xml.py index 2d805219..5309ebce 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -2,6 +2,7 @@ import os import re + import coverage from tests.coveragetest import CoverageTest @@ -24,6 +25,31 @@ class XmlTestHelpers(CoverageTest): self.start_import_stop(cov, "main") return cov + def make_tree(self, width, depth, curdir="."): + """Make a tree of packages. + + Makes `width` directories, named d0 .. d{width-1}. Each directory has + __init__.py, and `width` files, named f0.py .. f{width-1}.py. Each + directory also has `width` subdirectories, in the same fashion, until + a depth of `depth` is reached. + + """ + if depth == 0: + return + + def here(p): + """A path for `p` in our currently interesting directory.""" + return os.path.join(curdir, p) + + for i in range(width): + next_dir = here("d{0}".format(i)) + self.make_tree(width, depth-1, next_dir) + if curdir != ".": + self.make_file(here("__init__.py"), "") + for i in range(width): + filename = here("f{0}.py".format(i)) + self.make_file(filename, "# {0}\n".format(filename)) + class XmlReportTest(XmlTestHelpers, CoverageTest): """Tests of the XML reports from coverage.py.""" @@ -111,8 +137,51 @@ class XmlReportTest(XmlTestHelpers, CoverageTest): self.assertIn('line-rate="1"', init_line) +class XmlPackageStructureTest(XmlTestHelpers, CoverageTest): + """Tests about the package structure reported in the coverage.xml file.""" + + def test_packages(self): + self.make_tree(width=1, depth=3) + self.make_file("main.py", """\ + from d0.d0 import f0 + """) + cov = coverage.coverage(source=".") + self.start_import_stop(cov, "main") + cov.xml_report(outfile="-") + xml = self.stdout() + packages_and_classes = "".join(re_lines(xml, r"<package |<class ")) + scrubs = r' branch-rate="0"| complexity="0"| line-rate="[\d.]+"' + self.assertMultiLineEqual( + clean(packages_and_classes, scrubs), + clean("""\ + <package name="."> + <class filename="main.py" name="main.py"> + <package name="d0"> + <class filename="d0/__init__.py" name="__init__.py"> + <class filename="d0/f0.py" name="f0.py"> + <package name="d0.d0"> + <class filename="d0/d0/__init__.py" name="__init__.py"> + <class filename="d0/d0/f0.py" name="f0.py"> + """) + ) + + +def re_lines(text, pat): + """Return a list of lines that match `pat` in the string `text`.""" + lines = [l for l in text.splitlines(True) if re.search(pat, l)] + return lines + + def re_line(text, pat): """Return the one line in `text` that matches regex `pat`.""" - lines = [l for l in text.splitlines() if re.search(pat, l)] + lines = re_lines(text, pat) assert len(lines) == 1 return lines[0] + + +def clean(text, scrub=None): + """Remove any text matching `scrub`, and all leading whitespace.""" + if scrub: + text = re.sub(scrub, "", text) + text = re.sub(r"(?m)^\s+", "", text) + return text |