Source code for junitparser.xunit2
"""
The flavor based on Jenkins xunit plugin:
https://github.com/jenkinsci/xunit-plugin/blob/xunit-2.3.2/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
According to the internet, the schema is compatible with:
- Pytest (as default, though it also supports a "legacy" xunit1 flavor)
- Erlang/OTP
- Maven Surefire
- CppTest
There may be many others that I'm not aware of.
"""
import itertools
from typing import List, Type, TypeVar
from . import junitparser
[docs]
class StackTrace(junitparser.System):
_tag = "stackTrace"
[docs]
class InterimResult(junitparser.Result):
"""Base class for intermediate (rerun and flaky) test result (in contrast to JUnit FinalResult)."""
_tag = None
@property
def stack_trace(self):
"""<stackTrace>"""
elem = self.child(StackTrace)
if elem is not None:
return elem.text
return None
@stack_trace.setter
def stack_trace(self, value: str):
"""<stackTrace>"""
trace = self.child(StackTrace)
if trace is not None:
trace.text = value
else:
trace = StackTrace(value)
self.append(trace)
@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)
[docs]
class RerunFailure(InterimResult):
_tag = "rerunFailure"
[docs]
class RerunError(InterimResult):
_tag = "rerunError"
[docs]
class FlakyFailure(InterimResult):
_tag = "flakyFailure"
[docs]
class FlakyError(InterimResult):
_tag = "flakyError"
R = TypeVar("R", bound=InterimResult)
[docs]
class TestCase(junitparser.TestCase):
group = junitparser.Attr()
# XUnit2 TestCase children are JUnit children and intermediate results
ITER_TYPES = {
t._tag: t
for t in itertools.chain(
junitparser.TestCase.ITER_TYPES.values(),
(RerunFailure, RerunError, FlakyFailure, FlakyError),
)
}
def _interim_results(self, _type: Type[R]) -> List[R]:
return [entry for entry in self if isinstance(entry, _type)]
@property
def interim_result(self) -> List[InterimResult]:
"""
A list of interim results: :class:`RerunFailure`, :class:`RerunError`,
:class:`FlakyFailure`, or :class:`FlakyError` objects.
This is complementary to the result property returning final results.
"""
return self._interim_results(InterimResult)
[docs]
def rerun_failures(self) -> List[RerunFailure]:
"""<rerunFailure>"""
return self._interim_results(RerunFailure)
[docs]
def rerun_errors(self) -> List[RerunError]:
"""<rerunError>"""
return self._interim_results(RerunError)
[docs]
def flaky_failures(self) -> List[FlakyFailure]:
"""<flakyFailure>"""
return self._interim_results(FlakyFailure)
[docs]
def flaky_errors(self) -> List[FlakyError]:
"""<flakyError>"""
return self._interim_results(FlakyError)
@property
def is_rerun(self) -> bool:
"""Whether this testcase is rerun, i.e., there are rerun failures or errors."""
return any(self.rerun_failures()) or any(self.rerun_errors())
@property
def is_flaky(self) -> bool:
"""Whether this testcase is flaky, i.e., there are flaky failures or errors."""
return any(self.flaky_failures()) or any(self.flaky_errors())
[docs]
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)
[docs]
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)
[docs]
class JUnitXml(junitparser.JUnitXml):
# Pytest and xunit schema doesn't have "skipped" in testsuites
skipped = None
testsuite = TestSuite
[docs]
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)