Skip to content

Commit

Permalink
junitxml: add properties node in testsuite level
Browse files Browse the repository at this point in the history
The commit allow users to add a properties node in testsuite level see
example below:

<testsuite errors="0" failures="0" name="pytest" skips="1" tests="1"
time="11.824">
  <properties>
    <property name="ARCH" value="PPC"/>
    <property name="OS" value="RHEL 7.2"/>
    <property name="TestPlanURL" value="https://url.."/>
    <property name="Automated" value="True"/>
  </properties>
  <testcase classname="git.....>
  </testcase>
</testsuite>

The current situation is that properties node can be added to every
testcase node. However, sometimes we need some global properties that
applies to all testcases and give better description for the testsuite
itself.
  • Loading branch information
tareqalayan committed Mar 16, 2016
1 parent 5fd8207 commit fa6acdc
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Ronny Pfannschmidt
Ross Lawley
Ryan Wooden
Samuele Pedroni
Tareq Alayan
Tom Viner
Trevor Bekolay
Wouter van Ackooy
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
tests.
Thanks `@kalekundert`_ for the complete PR (`#1441`_).

* New Add ability to add global properties in the final xunit output file.
Thanks `@tareqalayan`_ for the complete PR `#1454`_).


*

**Changes**
Expand All @@ -28,10 +32,13 @@
.. _@milliams: https://github.com/milliams
.. _@novas0x2a: https://github.com/novas0x2a
.. _@kalekundert: https://github.com/kalekundert
.. _@tareqalayan: https://github.com/tareqalayan

.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
.. _#1441: https://github.com/pytest-dev/pytest/pull/1441
.. _#1454: https://github.com/pytest-dev/pytest/pull/1454


2.9.1.dev1
==========
Expand Down
21 changes: 21 additions & 0 deletions _pytest/junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ def __init__(self, logfile, prefix):
], 0)
self.node_reporters = {} # nodeid -> _NodeReporter
self.node_reporters_ordered = []
self.global_properties = []

def finalize(self, report):
nodeid = getattr(report, 'nodeid', report)
Expand All @@ -273,9 +274,12 @@ def node_reporter(self, report):
if key in self.node_reporters:
# TODO: breasks for --dist=each
return self.node_reporters[key]

reporter = _NodeReporter(nodeid, self)

self.node_reporters[key] = reporter
self.node_reporters_ordered.append(reporter)

return reporter

def add_stats(self, key):
Expand Down Expand Up @@ -361,7 +365,9 @@ def pytest_sessionfinish(self):
numtests = self.stats['passed'] + self.stats['failure']

logfile.write('<?xml version="1.0" encoding="utf-8"?>')

logfile.write(Junit.testsuite(
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
name="pytest",
errors=self.stats['error'],
Expand All @@ -374,3 +380,18 @@ def pytest_sessionfinish(self):
def pytest_terminal_summary(self, terminalreporter):
terminalreporter.write_sep("-",
"generated xml file: %s" % (self.logfile))

def add_global_property(self, name, value):
self.global_properties.append((str(name), bin_xml_escape(value)))

def _get_global_properties_node(self):
"""Return a Junit node containing custom properties, if any.
"""
if self.global_properties:
return Junit.properties(
[
Junit.property(name=name, value=value)
for name, value in self.global_properties
]
)
return ''
47 changes: 47 additions & 0 deletions doc/en/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,53 @@ This will add an extra property ``example_key="1"`` to the generated
Also please note that using this feature will break any schema verification.
This might be a problem when used with some CI servers.

LogXML: add_global_property
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. versionadded:: 2.10

If you want to add a properties node in the testsuite level, which may contains properties that are relevant
to all testcases you can use ``LogXML.add_global_properties``

.. code-block:: python
import pytest
@pytest.fixture(scope="session")
def log_global_env_facts(f):
if pytest.config.pluginmanager.hasplugin('junitxml'):
my_junit = getattr(pytest.config, '_xml', None)
my_junit.add_global_property('ARCH', 'PPC')
my_junit.add_global_property('STORAGE_TYPE', 'CEPH')
@pytest.mark.usefixtures(log_global_env_facts)
def start_and_prepare_env():
pass
class TestMe:
def test_foo(self):
assert True
This will add a property node below the testsuite node to the generated xml:

.. code-block:: xml
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="1" time="0.006">
<properties>
<property name="ARCH" value="PPC"/>
<property name="STORAGE_TYPE" value="CEPH"/>
</properties>
<testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
</testsuite>
.. 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.

Creating resultlog format files
----------------------------------------------------

Expand Down
35 changes: 35 additions & 0 deletions testing/test_junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,3 +783,38 @@ def test_pass():
u'test_fancy_items_regression test_pass'
u' test_fancy_items_regression.py',
]


def test_global_properties(testdir):
path = testdir.tmpdir.join("test_global_properties.xml")
log = LogXML(str(path), None)
from _pytest.runner import BaseReport

class Report(BaseReport):
sections = []
nodeid = "test_node_id"

log.pytest_sessionstart()
log.add_global_property('foo', 1)
log.add_global_property('bar', 2)
log.pytest_sessionfinish()

dom = minidom.parse(str(path))

properties = dom.getElementsByTagName('properties')

assert (properties.length == 1), "There must be one <properties> node"

property_list = dom.getElementsByTagName('property')

assert (property_list.length == 2), "There most be only 2 property nodes"

expected = {'foo': '1', 'bar': '2'}
actual = {}

for p in property_list:
k = str(p.getAttribute('name'))
v = str(p.getAttribute('value'))
actual[k] = v

assert actual == expected

0 comments on commit fa6acdc

Please sign in to comment.