diff options
Diffstat (limited to 'tests/test_xml.py')
-rw-r--r-- | tests/test_xml.py | 292 |
1 files changed, 175 insertions, 117 deletions
diff --git a/tests/test_xml.py b/tests/test_xml.py index 185e6ad1..09ab2f85 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -7,6 +7,7 @@ import os import os.path import re +from xml.etree import ElementTree import coverage from coverage.backward import import_local_file @@ -14,7 +15,6 @@ from coverage.files import abs_file from tests.coveragetest import CoverageTest from tests.goldtest import change_dir, compare, gold_path -from tests.helpers import re_line, re_lines class XmlTestHelpers(CoverageTest): @@ -30,7 +30,7 @@ class XmlTestHelpers(CoverageTest): self.make_file("sub/__init__.py") self.make_file("sub/doit.py", "print('doit!')") self.make_file("main.py", "import sub.doit") - cov = coverage.Coverage() + cov = coverage.Coverage(source=["."]) self.start_import_stop(cov, "main") return cov @@ -59,10 +59,36 @@ class XmlTestHelpers(CoverageTest): filename = here("f{0}.py".format(i)) self.make_file(filename, "# {0}\n".format(filename)) - def assert_source(self, xml, src): + def assert_source(self, xmldom, src): """Assert that the XML has a <source> element with `src`.""" src = abs_file(src) - self.assertRegex(xml, r'<source>\s*{0}\s*</source>'.format(re.escape(src))) + elts = xmldom.findall(".//sources/source") + assert any(elt.text == src for elt in elts) + + +class XmlTestHelpersTest(XmlTestHelpers, CoverageTest): + """Tests of methods in XmlTestHelpers.""" + + def test_assert_source(self): + dom = ElementTree.fromstring("""\ + <doc> + <src>foo</src> + <sources> + <source>{cwd}something</source> + <source>{cwd}another</source> + </sources> + </doc> + """.format(cwd=abs_file(".")+os.sep)) + + self.assert_source(dom, "something") + self.assert_source(dom, "another") + + with self.assertRaises(AssertionError): + self.assert_source(dom, "hello") + with self.assertRaises(AssertionError): + self.assert_source(dom, "foo") + with self.assertRaises(AssertionError): + self.assert_source(dom, "thing") class XmlReportTest(XmlTestHelpers, CoverageTest): @@ -110,25 +136,28 @@ class XmlReportTest(XmlTestHelpers, CoverageTest): def test_filename_format_showing_everything(self): cov = self.run_doit() - cov.xml_report(outfile="-") - xml = self.stdout() - doit_line = re_line(xml, "class.*doit") - self.assertIn('filename="sub/doit.py"', doit_line) + cov.xml_report() + dom = ElementTree.parse("coverage.xml") + elts = dom.findall(".//class[@name='doit.py']") + assert len(elts) == 1 + assert elts[0].get('filename') == "sub/doit.py" def test_filename_format_including_filename(self): cov = self.run_doit() - cov.xml_report(["sub/doit.py"], outfile="-") - xml = self.stdout() - doit_line = re_line(xml, "class.*doit") - self.assertIn('filename="sub/doit.py"', doit_line) + cov.xml_report(["sub/doit.py"]) + dom = ElementTree.parse("coverage.xml") + elts = dom.findall(".//class[@name='doit.py']") + assert len(elts) == 1 + assert elts[0].get('filename') == "sub/doit.py" def test_filename_format_including_module(self): cov = self.run_doit() import sub.doit # pylint: disable=import-error - cov.xml_report([sub.doit], outfile="-") - xml = self.stdout() - doit_line = re_line(xml, "class.*doit") - self.assertIn('filename="sub/doit.py"', doit_line) + cov.xml_report([sub.doit]) + dom = ElementTree.parse("coverage.xml") + elts = dom.findall(".//class[@name='doit.py']") + assert len(elts) == 1 + assert elts[0].get('filename') == "sub/doit.py" def test_reporting_on_nothing(self): # Used to raise a zero division error: @@ -136,28 +165,31 @@ class XmlReportTest(XmlTestHelpers, CoverageTest): self.make_file("empty.py", "") cov = coverage.Coverage() empty = self.start_import_stop(cov, "empty") - cov.xml_report([empty], outfile="-") - xml = self.stdout() - empty_line = re_line(xml, "class.*empty") - self.assertIn('filename="empty.py"', empty_line) - self.assertIn('line-rate="1"', empty_line) + cov.xml_report([empty]) + dom = ElementTree.parse("coverage.xml") + elts = dom.findall(".//class[@name='empty.py']") + assert len(elts) == 1 + assert elts[0].get('filename') == "empty.py" + assert elts[0].get('line-rate') == '1' def test_empty_file_is_100_not_0(self): # https://bitbucket.org/ned/coveragepy/issue/345 cov = self.run_doit() - cov.xml_report(outfile="-") - xml = self.stdout() - init_line = re_line(xml, 'filename="sub/__init__.py"') - self.assertIn('line-rate="1"', init_line) + cov.xml_report() + dom = ElementTree.parse("coverage.xml") + elts = dom.findall(".//class[@name='__init__.py']") + assert len(elts) == 1 + assert elts[0].get('line-rate') == '1' def test_curdir_source(self): # With no source= option, the XML report should explain that the source # is in the current directory. cov = self.run_doit() - cov.xml_report(outfile="-") - xml = self.stdout() - self.assert_source(xml, ".") - self.assertEqual(xml.count('<source>'), 1) + cov.xml_report() + dom = ElementTree.parse("coverage.xml") + self.assert_source(dom, ".") + sources = dom.findall(".//source") + assert len(sources) == 1 def test_deep_source(self): # When using source=, the XML report needs to mention those directories @@ -170,21 +202,33 @@ class XmlReportTest(XmlTestHelpers, CoverageTest): mod_foo = import_local_file("foo", "src/main/foo.py") # pragma: nested mod_bar = import_local_file("bar", "also/over/there/bar.py") # pragma: nested cov.stop() # pragma: nested - cov.xml_report([mod_foo, mod_bar], outfile="-") - xml = self.stdout() - - self.assert_source(xml, "src/main") - self.assert_source(xml, "also/over/there") - self.assertEqual(xml.count('<source>'), 2) - - self.assertIn( - '<class branch-rate="0" complexity="0" filename="foo.py" line-rate="1" name="foo.py">', - xml - ) - self.assertIn( - '<class branch-rate="0" complexity="0" filename="bar.py" line-rate="1" name="bar.py">', - xml - ) + cov.xml_report([mod_foo, mod_bar]) + dom = ElementTree.parse("coverage.xml") + + self.assert_source(dom, "src/main") + self.assert_source(dom, "also/over/there") + sources = dom.findall(".//source") + assert len(sources) == 2 + + foo_class = dom.findall(".//class[@name='foo.py']") + assert len(foo_class) == 1 + assert foo_class[0].attrib == { + 'branch-rate': '0', + 'complexity': '0', + 'filename': 'foo.py', + 'line-rate': '1', + 'name': 'foo.py', + } + + bar_class = dom.findall(".//class[@name='bar.py']") + assert len(bar_class) == 1 + assert bar_class[0].attrib == { + 'branch-rate': '0', + 'complexity': '0', + 'filename': 'bar.py', + 'line-rate': '1', + 'name': 'bar.py', + } def test_nonascii_directory(self): # https://bitbucket.org/ned/coveragepy/issues/573/cant-generate-xml-report-if-some-source @@ -195,22 +239,33 @@ class XmlReportTest(XmlTestHelpers, CoverageTest): cov.xml_report() +def unbackslash(v): + """Find strings in `v`, and replace backslashes with slashes throughout.""" + if isinstance(v, (tuple, list)): + return [unbackslash(vv) for vv in v] + elif isinstance(v, dict): + return {k: unbackslash(vv) for k, vv in v.items()} + else: + assert isinstance(v, str) + return v.replace("\\", "/") + + class XmlPackageStructureTest(XmlTestHelpers, CoverageTest): """Tests about the package structure reported in the coverage.xml file.""" def package_and_class_tags(self, cov): """Run an XML report on `cov`, and get the package and class tags.""" - self.captured_stdout.truncate(0) - cov.xml_report(outfile="-") - packages_and_classes = re_lines(self.stdout(), r"<package |<class ") - scrubs = r' branch-rate="0"| complexity="0"| line-rate="[\d.]+"' - return clean(packages_and_classes, scrubs) + cov.xml_report() + dom = ElementTree.parse("coverage.xml") + for node in dom.iter(): + if node.tag in ('package', 'class'): + yield (node.tag, {a:v for a,v in node.items() if a in ('name', 'filename')}) def assert_package_and_class_tags(self, cov, result): """Check the XML package and class tags from `cov` match `result`.""" - self.assertMultiLineEqual( - self.package_and_class_tags(cov), - clean(result) + self.assertEqual( + unbackslash(list(self.package_and_class_tags(cov))), + unbackslash(result), ) def test_package_names(self): @@ -220,18 +275,18 @@ class XmlPackageStructureTest(XmlTestHelpers, CoverageTest): """) cov = coverage.Coverage(source=".") self.start_import_stop(cov, "main") - self.assert_package_and_class_tags(cov, """\ - <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 test_package_depth(self): + self.assert_package_and_class_tags(cov, [ + ('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 test_package_depth_1(self): self.make_tree(width=1, depth=4) self.make_file("main.py", """\ from d0.d0 import f0 @@ -240,46 +295,62 @@ class XmlPackageStructureTest(XmlTestHelpers, CoverageTest): self.start_import_stop(cov, "main") cov.set_option("xml:package_depth", 1) - self.assert_package_and_class_tags(cov, """\ - <package name="."> - <class filename="main.py" name="main.py"> - <package name="d0"> - <class filename="d0/__init__.py" name="__init__.py"> - <class filename="d0/d0/__init__.py" name="d0/__init__.py"> - <class filename="d0/d0/d0/__init__.py" name="d0/d0/__init__.py"> - <class filename="d0/d0/d0/f0.py" name="d0/d0/f0.py"> - <class filename="d0/d0/f0.py" name="d0/f0.py"> - <class filename="d0/f0.py" name="f0.py"> + self.assert_package_and_class_tags(cov, [ + ('package', {'name': "."}), + ('class', {'filename': "main.py", 'name': "main.py"}), + ('package', {'name': "d0"}), + ('class', {'filename': "d0/__init__.py", 'name': "__init__.py"}), + ('class', {'filename': "d0/d0/__init__.py", 'name': "d0/__init__.py"}), + ('class', {'filename': "d0/d0/d0/__init__.py", 'name': "d0/d0/__init__.py"}), + ('class', {'filename': "d0/d0/d0/f0.py", 'name': "d0/d0/f0.py"}), + ('class', {'filename': "d0/d0/f0.py", 'name': "d0/f0.py"}), + ('class', {'filename': "d0/f0.py", 'name': "f0.py"}), + ]) + + def test_package_depth_2(self): + self.make_tree(width=1, depth=4) + self.make_file("main.py", """\ + from d0.d0 import f0 """) + cov = coverage.Coverage(source=".") + self.start_import_stop(cov, "main") cov.set_option("xml:package_depth", 2) - self.assert_package_and_class_tags(cov, """\ - <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/d0/__init__.py" name="d0/__init__.py"> - <class filename="d0/d0/d0/f0.py" name="d0/f0.py"> - <class filename="d0/d0/f0.py" name="f0.py"> + self.assert_package_and_class_tags(cov, [ + ('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/d0/__init__.py", 'name': "d0/__init__.py"}), + ('class', {'filename': "d0/d0/d0/f0.py", 'name': "d0/f0.py"}), + ('class', {'filename': "d0/d0/f0.py", 'name': "f0.py"}), + ]) + + def test_package_depth_3(self): + self.make_tree(width=1, depth=4) + self.make_file("main.py", """\ + from d0.d0 import f0 """) + cov = coverage.Coverage(source=".") + self.start_import_stop(cov, "main") cov.set_option("xml:package_depth", 3) - self.assert_package_and_class_tags(cov, """\ - <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"> - <package name="d0.d0.d0"> - <class filename="d0/d0/d0/__init__.py" name="__init__.py"> - <class filename="d0/d0/d0/f0.py" name="f0.py"> - """) + self.assert_package_and_class_tags(cov, [ + ('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"}), + ('package', {'name': "d0.d0.d0"}), + ('class', {'filename': "d0/d0/d0/__init__.py", 'name': "__init__.py"}), + ('class', {'filename': "d0/d0/d0/f0.py", 'name': "f0.py"}), + ]) def test_source_prefix(self): # https://bitbucket.org/ned/coveragepy/issues/465 @@ -287,26 +358,13 @@ class XmlPackageStructureTest(XmlTestHelpers, CoverageTest): self.make_file("src/mod.py", "print(17)") cov = coverage.Coverage(source=["src"]) self.start_import_stop(cov, "mod", modfile="src/mod.py") - self.assert_package_and_class_tags(cov, """\ - <package name="."> - <class filename="mod.py" name="mod.py"> - """) - xml = self.stdout() - self.assert_source(xml, "src") - - -def clean(text, scrub=None): - """Clean text to prepare it for comparison. - - Remove text matching `scrub`, and leading whitespace. Convert backslashes - to forward slashes. - """ - if scrub: - text = re.sub(scrub, "", text) - text = re.sub(r"(?m)^\s+", "", text) - text = re.sub(r"\\", "/", text) - return text + self.assert_package_and_class_tags(cov, [ + ('package', {'name': "."}), + ('class', {'filename': "mod.py", 'name': "mod.py"}), + ]) + dom = ElementTree.parse("coverage.xml") + self.assert_source(dom, "src") def compare_xml(expected, actual, **kwargs): |