Add `file` and `line` attributes to junit-xml output.
This adds the `file` and `line` attributes to the junit-xml output which can be used by tooling to identify where tests come from. This can be used for many things such as IDEs jumping to failures and test runners evenly balancing tests among multiple executors. Update test_junitxml.py Foo.
This commit is contained in:
parent
76497c2542
commit
7fa27af408
1
AUTHORS
1
AUTHORS
|
@ -39,6 +39,7 @@ Janne Vanhala
|
||||||
Jason R. Coombs
|
Jason R. Coombs
|
||||||
Jurko Gospodnetić
|
Jurko Gospodnetić
|
||||||
Katarzyna Jachim
|
Katarzyna Jachim
|
||||||
|
Kevin Cox
|
||||||
Maciek Fijalkowski
|
Maciek Fijalkowski
|
||||||
Maho
|
Maho
|
||||||
Marc Schlaich
|
Marc Schlaich
|
||||||
|
|
|
@ -67,6 +67,8 @@
|
||||||
|
|
||||||
- add a new ``--noconftest`` argument which ignores all ``conftest.py`` files.
|
- add a new ``--noconftest`` argument which ignores all ``conftest.py`` files.
|
||||||
|
|
||||||
|
- add ``file`` and ``line`` attributes to JUnit-XML output.
|
||||||
|
|
||||||
2.7.2 (compared to 2.7.1)
|
2.7.2 (compared to 2.7.1)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
""" report test results in JUnit-XML format, for use with Hudson and build integration servers.
|
""" report test results in JUnit-XML format, for use with Hudson and build integration servers.
|
||||||
|
|
||||||
|
Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
|
||||||
|
|
||||||
Based on initial code from Ross Lawley.
|
Based on initial code from Ross Lawley.
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
@ -93,11 +95,15 @@ class LogXML(object):
|
||||||
classnames = names[:-1]
|
classnames = names[:-1]
|
||||||
if self.prefix:
|
if self.prefix:
|
||||||
classnames.insert(0, self.prefix)
|
classnames.insert(0, self.prefix)
|
||||||
self.tests.append(Junit.testcase(
|
attrs = {
|
||||||
classname=".".join(classnames),
|
"classname": ".".join(classnames),
|
||||||
name=bin_xml_escape(names[-1]),
|
"name": bin_xml_escape(names[-1]),
|
||||||
time=0
|
"file": report.location[0],
|
||||||
))
|
"time": 0,
|
||||||
|
}
|
||||||
|
if report.location[1] is not None:
|
||||||
|
attrs["line"] = report.location[1]
|
||||||
|
self.tests.append(Junit.testcase(**attrs))
|
||||||
|
|
||||||
def _write_captured_output(self, report):
|
def _write_captured_output(self, report):
|
||||||
for capname in ('out', 'err'):
|
for capname in ('out', 'err'):
|
||||||
|
|
|
@ -70,6 +70,8 @@ class TestPython:
|
||||||
assert_attr(node, errors=1, tests=0)
|
assert_attr(node, errors=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_setup_error.py",
|
||||||
|
line="2",
|
||||||
classname="test_setup_error",
|
classname="test_setup_error",
|
||||||
name="test_function")
|
name="test_function")
|
||||||
fnode = tnode.getElementsByTagName("error")[0]
|
fnode = tnode.getElementsByTagName("error")[0]
|
||||||
|
@ -88,6 +90,8 @@ class TestPython:
|
||||||
assert_attr(node, skips=1)
|
assert_attr(node, skips=1)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_skip_contains_name_reason.py",
|
||||||
|
line="1",
|
||||||
classname="test_skip_contains_name_reason",
|
classname="test_skip_contains_name_reason",
|
||||||
name="test_skip")
|
name="test_skip")
|
||||||
snode = tnode.getElementsByTagName("skipped")[0]
|
snode = tnode.getElementsByTagName("skipped")[0]
|
||||||
|
@ -108,6 +112,8 @@ class TestPython:
|
||||||
assert_attr(node, failures=1)
|
assert_attr(node, failures=1)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_classname_instance.py",
|
||||||
|
line="1",
|
||||||
classname="test_classname_instance.TestClass",
|
classname="test_classname_instance.TestClass",
|
||||||
name="test_method")
|
name="test_method")
|
||||||
|
|
||||||
|
@ -120,6 +126,8 @@ class TestPython:
|
||||||
assert_attr(node, failures=1)
|
assert_attr(node, failures=1)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file=os.path.join("sub", "test_hello.py"),
|
||||||
|
line="0",
|
||||||
classname="sub.test_hello",
|
classname="sub.test_hello",
|
||||||
name="test_func")
|
name="test_func")
|
||||||
|
|
||||||
|
@ -151,6 +159,8 @@ class TestPython:
|
||||||
assert_attr(node, failures=1, tests=1)
|
assert_attr(node, failures=1, tests=1)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_failure_function.py",
|
||||||
|
line="1",
|
||||||
classname="test_failure_function",
|
classname="test_failure_function",
|
||||||
name="test_fail")
|
name="test_fail")
|
||||||
fnode = tnode.getElementsByTagName("failure")[0]
|
fnode = tnode.getElementsByTagName("failure")[0]
|
||||||
|
@ -193,6 +203,8 @@ class TestPython:
|
||||||
|
|
||||||
tnode = node.getElementsByTagName("testcase")[index]
|
tnode = node.getElementsByTagName("testcase")[index]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_failure_escape.py",
|
||||||
|
line="1",
|
||||||
classname="test_failure_escape",
|
classname="test_failure_escape",
|
||||||
name="test_func[%s]" % char)
|
name="test_func[%s]" % char)
|
||||||
sysout = tnode.getElementsByTagName('system-out')[0]
|
sysout = tnode.getElementsByTagName('system-out')[0]
|
||||||
|
@ -214,10 +226,14 @@ class TestPython:
|
||||||
assert_attr(node, failures=1, tests=2)
|
assert_attr(node, failures=1, tests=2)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_junit_prefixing.py",
|
||||||
|
line="0",
|
||||||
classname="xyz.test_junit_prefixing",
|
classname="xyz.test_junit_prefixing",
|
||||||
name="test_func")
|
name="test_func")
|
||||||
tnode = node.getElementsByTagName("testcase")[1]
|
tnode = node.getElementsByTagName("testcase")[1]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_junit_prefixing.py",
|
||||||
|
line="3",
|
||||||
classname="xyz.test_junit_prefixing."
|
classname="xyz.test_junit_prefixing."
|
||||||
"TestHello",
|
"TestHello",
|
||||||
name="test_hello")
|
name="test_hello")
|
||||||
|
@ -234,6 +250,8 @@ class TestPython:
|
||||||
assert_attr(node, skips=1, tests=0)
|
assert_attr(node, skips=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_xfailure_function.py",
|
||||||
|
line="1",
|
||||||
classname="test_xfailure_function",
|
classname="test_xfailure_function",
|
||||||
name="test_xfail")
|
name="test_xfail")
|
||||||
fnode = tnode.getElementsByTagName("skipped")[0]
|
fnode = tnode.getElementsByTagName("skipped")[0]
|
||||||
|
@ -253,6 +271,8 @@ class TestPython:
|
||||||
assert_attr(node, skips=1, tests=0)
|
assert_attr(node, skips=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_xfailure_xpass.py",
|
||||||
|
line="1",
|
||||||
classname="test_xfailure_xpass",
|
classname="test_xfailure_xpass",
|
||||||
name="test_xpass")
|
name="test_xpass")
|
||||||
fnode = tnode.getElementsByTagName("skipped")[0]
|
fnode = tnode.getElementsByTagName("skipped")[0]
|
||||||
|
@ -267,8 +287,10 @@ class TestPython:
|
||||||
assert_attr(node, errors=1, tests=0)
|
assert_attr(node, errors=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_collect_error.py",
|
||||||
#classname="test_collect_error",
|
#classname="test_collect_error",
|
||||||
name="test_collect_error")
|
name="test_collect_error")
|
||||||
|
assert tnode.getAttributeNode("line") is None
|
||||||
fnode = tnode.getElementsByTagName("error")[0]
|
fnode = tnode.getElementsByTagName("error")[0]
|
||||||
assert_attr(fnode, message="collection failure")
|
assert_attr(fnode, message="collection failure")
|
||||||
assert "SyntaxError" in fnode.toxml()
|
assert "SyntaxError" in fnode.toxml()
|
||||||
|
@ -281,8 +303,10 @@ class TestPython:
|
||||||
assert_attr(node, skips=1, tests=0)
|
assert_attr(node, skips=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
|
file="test_collect_skipped.py",
|
||||||
#classname="test_collect_error",
|
#classname="test_collect_error",
|
||||||
name="test_collect_skipped")
|
name="test_collect_skipped")
|
||||||
|
assert tnode.getAttributeNode("line") is None # py.test doesn't give us a line here.
|
||||||
fnode = tnode.getElementsByTagName("skipped")[0]
|
fnode = tnode.getElementsByTagName("skipped")[0]
|
||||||
assert_attr(fnode, message="collection skipped")
|
assert_attr(fnode, message="collection skipped")
|
||||||
|
|
||||||
|
@ -510,6 +534,7 @@ def test_unicode_issue368(testdir):
|
||||||
longrepr = ustr
|
longrepr = ustr
|
||||||
sections = []
|
sections = []
|
||||||
nodeid = "something"
|
nodeid = "something"
|
||||||
|
location = 'tests/filename.py', 42, 'TestClass.method'
|
||||||
report = Report()
|
report = Report()
|
||||||
|
|
||||||
# hopefully this is not too brittle ...
|
# hopefully this is not too brittle ...
|
||||||
|
|
Loading…
Reference in New Issue