Skip to content

Commit

Permalink
Fix type of suites and cases returned when iterating xunit2.JUnitXml
Browse files Browse the repository at this point in the history
  • Loading branch information
EnricoMi committed Dec 20, 2024
1 parent 5372679 commit 9bc58be
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 80 deletions.
8 changes: 5 additions & 3 deletions junitparser/junitparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,15 +495,17 @@ class TestSuite(Element):
skipped = IntAttr()
__test__ = False

testcase = TestCase

def __init__(self, name=None):
super().__init__(self._tag)
self.name = name
self.filepath = None

def __iter__(self):
return itertools.chain(
super().iterchildren(TestCase),
(case for suite in super().iterchildren(TestSuite) for case in suite),
super().iterchildren(self.testcase),
(case for suite in super().iterchildren(type(self)) for case in suite),
)

def __len__(self):
Expand Down Expand Up @@ -675,7 +677,7 @@ def __init__(self, name=None):
self.name = name

def __iter__(self):
return super().iterchildren(TestSuite)
return super().iterchildren(self.testsuite)

def __len__(self):
return len(list(self.__iter__()))
Expand Down
148 changes: 72 additions & 76 deletions junitparser/xunit2.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,82 +19,6 @@
T = TypeVar("T")


class TestSuite(junitparser.TestSuite):
"""TestSuite for Pytest, with some different attributes."""

group = junitparser.Attr()
id = junitparser.Attr()
package = junitparser.Attr()
file = junitparser.Attr()
log = junitparser.Attr()
url = junitparser.Attr()
version = junitparser.Attr()

def __iter__(self):
return itertools.chain(
super().iterchildren(TestCase),
(case for suite in super().iterchildren(TestSuite) for case in suite),
)

@property
def system_out(self):
"""<system-out>"""
elem = self.child(junitparser.SystemOut)
if elem is not None:
return elem.text
return None

@system_out.setter
def system_out(self, value: str):
"""<system-out>"""
out = self.child(junitparser.SystemOut)
if out is not None:
out.text = value
else:
out = junitparser.SystemOut(value)
self.append(out)

@property
def system_err(self):
"""<system-err>"""
elem = self.child(junitparser.SystemErr)
if elem is not None:
return elem.text
return None

@system_err.setter
def system_err(self, value: str):
"""<system-err>"""
err = self.child(junitparser.SystemErr)
if err is not None:
err.text = value
else:
err = junitparser.SystemErr(value)
self.append(err)


class JUnitXml(junitparser.JUnitXml):
# Pytest and xunit schema doesn't have "skipped" in testsuites
skipped = None

testsuite = TestSuite

def update_statistics(self):
"""Update test count, time, etc."""
time = 0
tests = failures = errors = 0
for suite in self:
suite.update_statistics()
tests += suite.tests
failures += suite.failures
errors += suite.errors
time += suite.time
self.tests = tests
self.failures = failures
self.errors = errors
self.time = round(time, 3)


class StackTrace(junitparser.System):
_tag = "stackTrace"

Expand Down Expand Up @@ -202,3 +126,75 @@ def flaky_errors(self):
def add_rerun_result(self, result: RerunType):
"""Append a rerun result to the testcase. A testcase can have multiple rerun results."""
self.append(result)


class TestSuite(junitparser.TestSuite):
"""TestSuite for Pytest, with some different attributes."""

group = junitparser.Attr()
id = junitparser.Attr()
package = junitparser.Attr()
file = junitparser.Attr()
log = junitparser.Attr()
url = junitparser.Attr()
version = junitparser.Attr()

testcase = TestCase

@property
def system_out(self):
"""<system-out>"""
elem = self.child(junitparser.SystemOut)
if elem is not None:
return elem.text
return None

@system_out.setter
def system_out(self, value: str):
"""<system-out>"""
out = self.child(junitparser.SystemOut)
if out is not None:
out.text = value
else:
out = junitparser.SystemOut(value)
self.append(out)

@property
def system_err(self):
"""<system-err>"""
elem = self.child(junitparser.SystemErr)
if elem is not None:
return elem.text
return None

@system_err.setter
def system_err(self, value: str):
"""<system-err>"""
err = self.child(junitparser.SystemErr)
if err is not None:
err.text = value
else:
err = junitparser.SystemErr(value)
self.append(err)


class JUnitXml(junitparser.JUnitXml):
# Pytest and xunit schema doesn't have "skipped" in testsuites
skipped = None

testsuite = TestSuite

def update_statistics(self):
"""Update test count, time, etc."""
time = 0
tests = failures = errors = 0
for suite in self:
suite.update_statistics()
tests += suite.tests
failures += suite.failures
errors += suite.errors
time += suite.time
self.tests = tests
self.failures = failures
self.errors = errors
self.time = round(time, 3)
29 changes: 28 additions & 1 deletion tests/test_xunit2.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def test_case_fromstring(self):
<system-err>System err</system-err>
</testcase>"""
case = TestCase.fromstring(text)
assert isinstance(case, TestCase)
assert case.name == "testname"
assert isinstance(case.result[0], Failure)
assert case.system_out == "System out"
Expand Down Expand Up @@ -103,12 +104,38 @@ def test_init(self):
assert xml.tostring().count(b"skipped") == 1

def test_fromstring(self):
text = """<testsuites><testsuite name="suitename1">
<testcase name="testname1">
</testcase></testsuite>
<testsuite name="suitename2">
<testcase name="testname2"/>
<testcase name="testname3">
</testcase></testsuite></testsuites>"""
xml = JUnitXml.fromstring(text)
assert isinstance(xml, JUnitXml)
suites = list(xml)
assert len(suites) == 2
suite1, suite2 = suites
assert isinstance(suite1, TestSuite)
assert isinstance(suite2, TestSuite)
assert suite1.name == "suitename1"
assert suite2.name == "suitename2"
cases = list(suite2)
assert len(cases) == 2
assert isinstance(cases[0], TestCase)
assert isinstance(cases[1], TestCase)
assert [test.name for test in cases] == ["testname2", "testname3"]

def test_suite_fromstring(self):
text = """<testsuite name="suite name">
<testcase name="test name 1"/>
<testcase name="test name 2"/>
</testsuite>"""
suite = JUnitXml.fromstring(text)
assert isinstance(suite, TestSuite)
assert suite.name == "suite name"
assert len(list(suite)) == 2
cases = list(suite)
assert len(cases) == 2
assert isinstance(cases[0], TestCase)
assert isinstance(cases[1], TestCase)
assert [test.name for test in suite] == ["test name 1", "test name 2"]

0 comments on commit 9bc58be

Please sign in to comment.