diff --git a/README.rst b/README.rst index a082467..79b6517 100644 --- a/README.rst +++ b/README.rst @@ -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. diff --git a/junitparser/junitparser.py b/junitparser/junitparser.py index d77ef5a..5ca6be9 100644 --- a/junitparser/junitparser.py +++ b/junitparser/junitparser.py @@ -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. @@ -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` """ diff --git a/junitparser/xunit2.py b/junitparser/xunit2.py index 4f60827..5f03a6a 100644 --- a/junitparser/xunit2.py +++ b/junitparser/xunit2.py @@ -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.""" diff --git a/tests/test_xunit2.py b/tests/test_xunit2.py index 13d439e..904b788 100644 --- a/tests/test_xunit2.py +++ b/tests/test_xunit2.py @@ -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 @@ -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("""\ + + + + + + + a line of output + another line + + + """) + 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("""\ + + + + + + + + """ + ) + 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'', + b'', + ] class Test_TestSuite: def test_properties(self):