Skip to content

Commit ceacc12

Browse files
committed
Merge pull request #1454 from tareqalayan/add-global-properties-node
junitxml: add properties node in testsuite level
2 parents 5fd8207 + fa6acdc commit ceacc12

File tree

5 files changed

+111
-0
lines changed

5 files changed

+111
-0
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Ronny Pfannschmidt
7575
Ross Lawley
7676
Ryan Wooden
7777
Samuele Pedroni
78+
Tareq Alayan
7879
Tom Viner
7980
Trevor Bekolay
8081
Wouter van Ackooy

CHANGELOG.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
tests.
1616
Thanks `@kalekundert`_ for the complete PR (`#1441`_).
1717

18+
* New Add ability to add global properties in the final xunit output file.
19+
Thanks `@tareqalayan`_ for the complete PR `#1454`_).
20+
21+
1822
*
1923

2024
**Changes**
@@ -28,10 +32,13 @@
2832
.. _@milliams: https://github.com/milliams
2933
.. _@novas0x2a: https://github.com/novas0x2a
3034
.. _@kalekundert: https://github.com/kalekundert
35+
.. _@tareqalayan: https://github.com/tareqalayan
3136

3237
.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
3338
.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
3439
.. _#1441: https://github.com/pytest-dev/pytest/pull/1441
40+
.. _#1454: https://github.com/pytest-dev/pytest/pull/1454
41+
3542

3643
2.9.1.dev1
3744
==========

_pytest/junitxml.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ def __init__(self, logfile, prefix):
254254
], 0)
255255
self.node_reporters = {} # nodeid -> _NodeReporter
256256
self.node_reporters_ordered = []
257+
self.global_properties = []
257258

258259
def finalize(self, report):
259260
nodeid = getattr(report, 'nodeid', report)
@@ -273,9 +274,12 @@ def node_reporter(self, report):
273274
if key in self.node_reporters:
274275
# TODO: breasks for --dist=each
275276
return self.node_reporters[key]
277+
276278
reporter = _NodeReporter(nodeid, self)
279+
277280
self.node_reporters[key] = reporter
278281
self.node_reporters_ordered.append(reporter)
282+
279283
return reporter
280284

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

363367
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
368+
364369
logfile.write(Junit.testsuite(
370+
self._get_global_properties_node(),
365371
[x.to_xml() for x in self.node_reporters_ordered],
366372
name="pytest",
367373
errors=self.stats['error'],
@@ -374,3 +380,18 @@ def pytest_sessionfinish(self):
374380
def pytest_terminal_summary(self, terminalreporter):
375381
terminalreporter.write_sep("-",
376382
"generated xml file: %s" % (self.logfile))
383+
384+
def add_global_property(self, name, value):
385+
self.global_properties.append((str(name), bin_xml_escape(value)))
386+
387+
def _get_global_properties_node(self):
388+
"""Return a Junit node containing custom properties, if any.
389+
"""
390+
if self.global_properties:
391+
return Junit.properties(
392+
[
393+
Junit.property(name=name, value=value)
394+
for name, value in self.global_properties
395+
]
396+
)
397+
return ''

doc/en/usage.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,53 @@ This will add an extra property ``example_key="1"`` to the generated
193193
Also please note that using this feature will break any schema verification.
194194
This might be a problem when used with some CI servers.
195195

196+
LogXML: add_global_property
197+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
198+
199+
.. versionadded:: 2.10
200+
201+
If you want to add a properties node in the testsuite level, which may contains properties that are relevant
202+
to all testcases you can use ``LogXML.add_global_properties``
203+
204+
.. code-block:: python
205+
206+
import pytest
207+
208+
@pytest.fixture(scope="session")
209+
def log_global_env_facts(f):
210+
211+
if pytest.config.pluginmanager.hasplugin('junitxml'):
212+
my_junit = getattr(pytest.config, '_xml', None)
213+
214+
my_junit.add_global_property('ARCH', 'PPC')
215+
my_junit.add_global_property('STORAGE_TYPE', 'CEPH')
216+
217+
@pytest.mark.usefixtures(log_global_env_facts)
218+
def start_and_prepare_env():
219+
pass
220+
221+
class TestMe:
222+
def test_foo(self):
223+
assert True
224+
225+
This will add a property node below the testsuite node to the generated xml:
226+
227+
.. code-block:: xml
228+
229+
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="1" time="0.006">
230+
<properties>
231+
<property name="ARCH" value="PPC"/>
232+
<property name="STORAGE_TYPE" value="CEPH"/>
233+
</properties>
234+
<testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
235+
</testsuite>
236+
237+
.. warning::
238+
239+
This is an experimental feature, and its interface might be replaced
240+
by something more powerful and general in future versions. The
241+
functionality per-se will be kept.
242+
196243
Creating resultlog format files
197244
----------------------------------------------------
198245

testing/test_junitxml.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,3 +783,38 @@ def test_pass():
783783
u'test_fancy_items_regression test_pass'
784784
u' test_fancy_items_regression.py',
785785
]
786+
787+
788+
def test_global_properties(testdir):
789+
path = testdir.tmpdir.join("test_global_properties.xml")
790+
log = LogXML(str(path), None)
791+
from _pytest.runner import BaseReport
792+
793+
class Report(BaseReport):
794+
sections = []
795+
nodeid = "test_node_id"
796+
797+
log.pytest_sessionstart()
798+
log.add_global_property('foo', 1)
799+
log.add_global_property('bar', 2)
800+
log.pytest_sessionfinish()
801+
802+
dom = minidom.parse(str(path))
803+
804+
properties = dom.getElementsByTagName('properties')
805+
806+
assert (properties.length == 1), "There must be one <properties> node"
807+
808+
property_list = dom.getElementsByTagName('property')
809+
810+
assert (property_list.length == 2), "There most be only 2 property nodes"
811+
812+
expected = {'foo': '1', 'bar': '2'}
813+
actual = {}
814+
815+
for p in property_list:
816+
k = str(p.getAttribute('name'))
817+
v = str(p.getAttribute('value'))
818+
actual[k] = v
819+
820+
assert actual == expected

0 commit comments

Comments
 (0)