Merge pull request #956 from nicoddemus/record-xml-property
add preliminary support for extended junit xml properties
This commit is contained in:
commit
b25e41e348
1
AUTHORS
1
AUTHORS
|
@ -61,3 +61,4 @@ Samuele Pedroni
|
||||||
Tom Viner
|
Tom Viner
|
||||||
Trevor Bekolay
|
Trevor Bekolay
|
||||||
Wouter van Ackooy
|
Wouter van Ackooy
|
||||||
|
David Díaz-Barquero
|
||||||
|
|
|
@ -147,6 +147,9 @@
|
||||||
- fix issue890: changed extension of all documentation files from ``txt`` to
|
- fix issue890: changed extension of all documentation files from ``txt`` to
|
||||||
``rst``. Thanks to Abhijeet for the PR.
|
``rst``. Thanks to Abhijeet for the PR.
|
||||||
|
|
||||||
|
- issue951: add new record_xml_property fixture, that supports logging
|
||||||
|
additional information on xml output. Thanks David Diaz for the PR.
|
||||||
|
|
||||||
2.7.3 (compared to 2.7.2)
|
2.7.3 (compared to 2.7.2)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import pytest
|
||||||
|
|
||||||
# Python 2.X and 3.X compatibility
|
# Python 2.X and 3.X compatibility
|
||||||
if sys.version_info[0] < 3:
|
if sys.version_info[0] < 3:
|
||||||
|
@ -53,6 +54,20 @@ def bin_xml_escape(arg):
|
||||||
return unicode('#x%04X') % i
|
return unicode('#x%04X') % i
|
||||||
return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg)))
|
return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg)))
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def record_xml_property(request):
|
||||||
|
"""Fixture that adds extra xml properties to the tag for the calling test.
|
||||||
|
The fixture is callable with (name, value), with value being automatically
|
||||||
|
xml-encoded.
|
||||||
|
"""
|
||||||
|
def inner(name, value):
|
||||||
|
if hasattr(request.config, "_xml"):
|
||||||
|
request.config._xml.add_custom_property(name, value)
|
||||||
|
msg = 'record_xml_property is an experimental feature'
|
||||||
|
request.config.warn(code='C3', message=msg,
|
||||||
|
fslocation=request.node.location[:2])
|
||||||
|
return inner
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup("terminal reporting")
|
group = parser.getgroup("terminal reporting")
|
||||||
group.addoption('--junitxml', '--junit-xml', action="store",
|
group.addoption('--junitxml', '--junit-xml', action="store",
|
||||||
|
@ -75,7 +90,6 @@ def pytest_unconfigure(config):
|
||||||
del config._xml
|
del config._xml
|
||||||
config.pluginmanager.unregister(xml)
|
config.pluginmanager.unregister(xml)
|
||||||
|
|
||||||
|
|
||||||
def mangle_testnames(names):
|
def mangle_testnames(names):
|
||||||
names = [x.replace(".py", "") for x in names if x != '()']
|
names = [x.replace(".py", "") for x in names if x != '()']
|
||||||
names[0] = names[0].replace("/", '.')
|
names[0] = names[0].replace("/", '.')
|
||||||
|
@ -89,6 +103,10 @@ class LogXML(object):
|
||||||
self.tests = []
|
self.tests = []
|
||||||
self.passed = self.skipped = 0
|
self.passed = self.skipped = 0
|
||||||
self.failed = self.errors = 0
|
self.failed = self.errors = 0
|
||||||
|
self.custom_properties = {}
|
||||||
|
|
||||||
|
def add_custom_property(self, name, value):
|
||||||
|
self.custom_properties[str(name)] = bin_xml_escape(str(value))
|
||||||
|
|
||||||
def _opentestcase(self, report):
|
def _opentestcase(self, report):
|
||||||
names = mangle_testnames(report.nodeid.split("::"))
|
names = mangle_testnames(report.nodeid.split("::"))
|
||||||
|
@ -118,6 +136,10 @@ class LogXML(object):
|
||||||
def append(self, obj):
|
def append(self, obj):
|
||||||
self.tests[-1].append(obj)
|
self.tests[-1].append(obj)
|
||||||
|
|
||||||
|
def append_custom_properties(self):
|
||||||
|
self.tests[-1].attr.__dict__.update(self.custom_properties)
|
||||||
|
self.custom_properties.clear()
|
||||||
|
|
||||||
def append_pass(self, report):
|
def append_pass(self, report):
|
||||||
self.passed += 1
|
self.passed += 1
|
||||||
self._write_captured_output(report)
|
self._write_captured_output(report)
|
||||||
|
@ -179,6 +201,7 @@ class LogXML(object):
|
||||||
if report.when == "setup":
|
if report.when == "setup":
|
||||||
self._opentestcase(report)
|
self._opentestcase(report)
|
||||||
self.tests[-1].attr.time += getattr(report, 'duration', 0)
|
self.tests[-1].attr.time += getattr(report, 'duration', 0)
|
||||||
|
self.append_custom_properties()
|
||||||
if report.passed:
|
if report.passed:
|
||||||
if report.when == "call": # ignore setup/teardown
|
if report.when == "call": # ignore setup/teardown
|
||||||
self.append_pass(report)
|
self.append_pass(report)
|
||||||
|
|
|
@ -6,7 +6,7 @@ def get_version_string():
|
||||||
fn = py.path.local(__file__).join("..", "..", "..",
|
fn = py.path.local(__file__).join("..", "..", "..",
|
||||||
"_pytest", "__init__.py")
|
"_pytest", "__init__.py")
|
||||||
for line in fn.readlines():
|
for line in fn.readlines():
|
||||||
if "version" in line:
|
if "version" in line and not line.strip().startswith('#'):
|
||||||
return eval(line.split("=")[-1])
|
return eval(line.split("=")[-1])
|
||||||
|
|
||||||
def get_minor_version_string():
|
def get_minor_version_string():
|
||||||
|
|
|
@ -153,6 +153,36 @@ integration servers, use this invocation::
|
||||||
|
|
||||||
to create an XML file at ``path``.
|
to create an XML file at ``path``.
|
||||||
|
|
||||||
|
record_xml_property
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. versionadded:: 2.8
|
||||||
|
|
||||||
|
If you want to log additional information for a test, you can use the
|
||||||
|
``record_xml_property`` fixture:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def test_function(record_xml_property):
|
||||||
|
record_xml_property("example_key", 1)
|
||||||
|
assert 0
|
||||||
|
|
||||||
|
This will add an extra property ``example_key="1"`` to the generated
|
||||||
|
``testcase`` tag:
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<testcase classname="test_function" example_key="1" file="test_function.py" line="0" name="test_function" time="0.0009">
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an experimental feature, and its interface might be replaced
|
||||||
|
by something more powerful and general in future versions. The
|
||||||
|
functionality per-se will be kept, however.
|
||||||
|
|
||||||
|
Also please note that using this feature will break any schema verification.
|
||||||
|
This might be a problem when used with some CI servers.
|
||||||
|
|
||||||
Creating resultlog format files
|
Creating resultlog format files
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -553,4 +553,13 @@ def test_unicode_issue368(testdir):
|
||||||
log.append_skipped(report)
|
log.append_skipped(report)
|
||||||
log.pytest_sessionfinish()
|
log.pytest_sessionfinish()
|
||||||
|
|
||||||
|
def test_record_property(testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
def test_record(record_xml_property):
|
||||||
|
record_xml_property("foo", "<1");
|
||||||
|
""")
|
||||||
|
result, dom = runandparse(testdir, '-rw')
|
||||||
|
node = dom.getElementsByTagName("testsuite")[0]
|
||||||
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
|
assert_attr(tnode, foo="<1")
|
||||||
|
result.stdout.fnmatch_lines('*C3*test_record_property.py*experimental*')
|
||||||
|
|
Loading…
Reference in New Issue