Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,24 @@ Junitparser also support extra schemas:

.. code-block:: python

# Extended with extra properties/attributes from the xunit2 schema.
from junitparser.xunit2 import TestCase, TestSuite, RerunFailure
# These classes are redefined to support extra properties and attributes
# of the xunit2 schema.

# TestSuite supports system_err.
suite = TestSuite("mySuite")
suite.system_err = "System err" # xunit2 specific property
suite.system_err = "System err"

# TestCase supports interim results.
case = TestCase("myCase")
rerun_failure = RerunFailure("Not found", "404") # case property
rerun_failure = RerunFailure("Not found", "404")
rerun_failure.stack_trace = "Stack"
rerun_failure.system_err = "E404"
rerun_failure.system_out = "NOT FOUND"
case.add_interim_result(rerun_failure)

# TestCase supports properties.
case.add_property("cmake_labels", "cuda;tier2")

Currently supported schemas including:

- xunit2_, supported by pytest, Erlang/OTP, Maven Surefire, CppTest, etc.
Expand Down
4 changes: 2 additions & 2 deletions junitparser/junitparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ def system_err(self, value: str):


class Property(Element):
"""A key/value pare that's stored in the testsuite.
"""A key/value pair that's stored in the testsuite or testcase properties.

Use it to store anything you find interesting or useful.

Expand Down Expand Up @@ -458,7 +458,7 @@ def __lt__(self, other):


class Properties(Element):
"""A list of properties inside a testsuite.
"""A list of properties inside a testsuite or testcase.

See :class:`Property`
"""
Expand Down
30 changes: 30 additions & 0 deletions junitparser/xunit2.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,36 @@ def add_interim_result(self, result: InterimResult):
"""Append an interim (rerun or flaky) result to the testcase. A testcase can have multiple interim results."""
self.append(result)

def add_property(self, name: str, value: str):
"""Add a property *name* = *value* to the testcase.

See :class:`junitparser.Property` and :class:`junitparser.Properties`.
"""

props = self.child(junitparser.Properties)
if props is None:
props = junitparser.Properties()
self.append(props)
prop = junitparser.Property(name, value)
props.add_property(prop)

def properties(self):
"""Iterate through all :class:`junitparser.Property` elements in the testcase."""
props = self.child(junitparser.Properties)
if props is None:
return
for prop in props:
yield prop

def remove_property(self, property_: junitparser.Property):
"""Remove property *property_* from the testcase."""
props = self.child(junitparser.Properties)
if props is None:
return
for prop in props:
if prop == property_:
props.remove(property_)


class TestSuite(junitparser.TestSuite):
"""TestSuite for Pytest, with some different attributes."""
Expand Down
59 changes: 58 additions & 1 deletion tests/test_xunit2.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import textwrap
from junitparser.xunit2 import JUnitXml, TestSuite, TestCase, RerunFailure, RerunError, FlakyFailure, FlakyError
from junitparser import Failure
from junitparser import Failure, Property
from copy import deepcopy


Expand Down Expand Up @@ -137,6 +138,62 @@ def test_case_rerun(self):
case.add_interim_result(failure2)
assert len(case.rerun_failures()) == 2

def test_properties_and_output(self):
text = textwrap.dedent("""\
<testcase name="test_pushstringvector" classname="test_pushstringvector" status="run">
<properties>
<property name="cmake_labels" value="util;script"/>
<property name="TestType" value="LATENCY_EDMA" />
</properties>
<system-out>
a line of output
another line
</system-out>
</testcase>
""")
case = TestCase.fromstring(text)
assert case.name == "test_pushstringvector"
assert case.system_out == "\na line of output\nanother line\n"
# Check that there are two properties in the TestCase, then check the values.
case_properties = list(case.properties())
assert len(case_properties) == 2
prop1, prop2 = case_properties
assert prop1.name == "cmake_labels" and prop1.value == "util;script"
assert prop2.name == "TestType" and prop2.value == "LATENCY_EDMA"

def test_suite_parses_testcase_properties(self):
text = textwrap.dedent("""\
<testsuite name="suitename1" tests="1" failures="0">
<testcase name="testname1">
<properties>
<property name="labels" value="foo;bar"/>
<property name="test_type" value="latency" />
</properties>
</testcase>
</testsuite>"""
)
test_suite1 = TestSuite.fromstring(text)
assert test_suite1.name == "suitename1"
cases = list(iter(test_suite1))
assert len(cases) == 1
case = cases[0]
case_properties = list(case.properties())
assert len(case_properties) == 2
prop1, prop2 = case_properties
assert prop1.name == "labels" and prop1.value == "foo;bar"
assert prop2.name == "test_type" and prop2.value == "latency"

def test_add_remove_property(self):
case = TestCase()
case.add_property("prop1", "foo")
case.add_property("prop2", "bar")
prop_to_remove = Property("prop1", "foo")
case.remove_property(prop_to_remove)
assert len(list(case.properties())) == 1
assert case.tostring() in [
b'<testcase><properties><property name="prop2" value="bar" /></properties></testcase>',
b'<testcase><properties><property name="prop2" value="bar"/></properties></testcase>',
]

class Test_TestSuite:
def test_properties(self):
Expand Down