diff --git a/CHANGELOG b/CHANGELOG index 01ad82386..6801d325f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ 2.8.6.dev1 ---------- +- fix #1259: allow for double nodeids in junitxml 2.8.5 ----- diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index 1c114ddcb..995694687 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -71,7 +71,6 @@ class _NodeReporter(object): self.testcase = None self.attrs = {} - def append(self, node): self.xml.add_stats(type(node).__name__) self.nodes.append(node) @@ -82,7 +81,6 @@ class _NodeReporter(object): self.property_insert_order.append(name) self.properties[name] = bin_xml_escape(value) - def make_properties_node(self): """Return a Junit node containing custom properties, if any. """ @@ -93,7 +91,6 @@ class _NodeReporter(object): ]) return '' - def record_testreport(self, testreport): assert not self.testcase names = mangle_testnames(testreport.nodeid.split("::")) @@ -182,7 +179,6 @@ class _NodeReporter(object): message=skipreason)) self._write_captured_output(report) - def finalize(self): data = self.to_xml().unicode(indent=0) self.__dict__.clear() @@ -262,6 +258,14 @@ class LogXML(object): self.node_reporters = {} # nodeid -> _NodeReporter self.node_reporters_ordered = [] + def finalize(self, report): + nodeid = getattr(report, 'nodeid', report) + # local hack to handle xdist report order + slavenode = getattr(report, 'node', None) + reporter = self.node_reporters.pop((nodeid, slavenode)) + if reporter is not None: + reporter.finalize() + def node_reporter(self, report): nodeid = getattr(report, 'nodeid', report) # local hack to handle xdist report order @@ -270,7 +274,7 @@ class LogXML(object): key = nodeid, slavenode if key in self.node_reporters: - #TODO: breasks for --dist=each + # TODO: breasks for --dist=each return self.node_reporters[key] reporter = _NodeReporter(nodeid, self) self.node_reporters[key] = reporter @@ -324,7 +328,7 @@ class LogXML(object): reporter.append_skipped(report) self.update_testcase_duration(report) if report.when == "teardown": - self.node_reporter(report).finalize() + self.finalize(report) def update_testcase_duration(self, report): """accumulates total duration for nodeid from given report and updates diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 81a8f4e6a..3ecad4926 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -18,6 +18,7 @@ def runandparse(testdir, *args): def assert_attr(node, **kwargs): __tracebackhide__ = True + def nodeval(node, name): anode = node.getAttributeNode(name) if anode is not None: @@ -667,10 +668,12 @@ def test_runs_twice(testdir): pass ''') - result = testdir.runpytest(f, f, '--junitxml', testdir.tmpdir.join("test.xml")) - assert 'INTERNALERROR' not in str(result.stdout) + result = testdir.runpytest( + f, f, '--junitxml', testdir.tmpdir.join("test.xml")) + assert 'INTERNALERROR' not in result.stdout.str() +@pytest.mark.xfail(reason='hangs', run=False) def test_runs_twice_xdist(testdir): pytest.importorskip('xdist') f = testdir.makepyfile(''' @@ -678,7 +681,41 @@ def test_runs_twice_xdist(testdir): pass ''') - result = testdir.runpytest(f, + result = testdir.runpytest( + f, '--dist', 'each', '--tx', '2*popen', '--junitxml', testdir.tmpdir.join("test.xml")) - assert 'INTERNALERROR' not in str(result.stdout) \ No newline at end of file + assert 'INTERNALERROR' not in result.stdout.str() + + +def test_fancy_items_regression(testdir): + ' issue 1259' + testdir.makeconftest(""" + import pytest + class FunItem(pytest.Item): + def runtest(self): + pass + class NoFunItem(pytest.Item): + def runtest(self): + pass + + class FunCollector(pytest.File): + def collect(self): + return [ + FunItem('a', self), + NoFunItem('a', self), + ] + + def pytest_collect_file(path, parent): + return FunCollector(path, parent) + """) + + testdir.makepyfile(''' + def test_pass(): + pass + ''') + + result = testdir.runpytest( + '--junitxml', testdir.tmpdir.join("test.xml")) + + assert 'INTERNALERROR' not in result.stdout.str()