def test_parse_error_from_xml_parse_error(self): error = XmlParseError('xml parse error') error.code = 123 error.position = (1, 2) actual = ParseError.from_exception('file', error) expected = ParseError('file', 'xml parse error', 1, 2) self.assertEqual(expected, actual)
def test_parse_junit_xml_files_with_non_existing_file(self): self.assertEqual( parse_junit_xml_files(['files/does_not_exist.xml']), ParsedUnitTestResults( cases=[], files=1, errors=[ParseError('files/does_not_exist.xml', 'File does not exist.', None, None)], suite_errors=0, suite_failures=0, suite_skipped=0, suite_tests=0, suite_time=0, suites=0 ))
def test_parse_junit_xml_files_with_non_junit_file(self): self.assertEqual( parse_junit_xml_files(['files/non-junit.xml']), ParsedUnitTestResults( files=1, errors=[ParseError(file='files/non-junit.xml', message='Invalid format.', line=None, column=None)], suites=0, suite_tests=0, suite_skipped=0, suite_failures=0, suite_errors=0, suite_time=0, cases=[] ))
def test_parse_junit_xml_files_with_corrupt_xml_file(self): self.assertEqual( parse_junit_xml_files(['files/corrupt-xml.xml']), ParsedUnitTestResults( files=1, errors=[ParseError(file='files/corrupt-xml.xml', message='File is not a valid XML file:\nno element found: line 11, column 21', line=11, column=21)], suites=0, suite_tests=0, suite_skipped=0, suite_failures=0, suite_errors=0, suite_time=0, cases=[] ))
def test_parse_junit_xml_files_with_non_xml_file(self): self.assertEqual( parse_junit_xml_files(['files/non-xml.xml']), ParsedUnitTestResults( files=1, errors=[ParseError(file='files/non-xml.xml', message='File is not a valid XML file:\nsyntax error: line 1, column 0', line=1, column=0)], suites=0, suite_tests=0, suite_skipped=0, suite_failures=0, suite_errors=0, suite_time=0, cases=[] ))
def test_parse_junit_xml_files_with_empty_file(self): self.assertEqual( parse_junit_xml_files(['files/empty.xml']), ParsedUnitTestResults( cases=[], files=1, errors=[ParseError('files/empty.xml', 'File is empty.', None, None)], suite_errors=0, suite_failures=0, suite_skipped=0, suite_tests=0, suite_time=0, suites=0 ))
def test_get_conclusion_parse_errors(self): for fail_on_errors in [True, False]: for fail_on_failures in [True, False]: with self.subTest(fail_on_errors=fail_on_errors, fail_on_failures=fail_on_failures): actual = get_conclusion(ParsedUnitTestResults( files=2, errors=[ParseError(file='file', message='error', line=None, column=None)], suites=1, suite_tests=4, suite_skipped=1, suite_failures=0, suite_errors=0, suite_time=10, cases=[] ), fail_on_errors=fail_on_errors, fail_on_failures=fail_on_failures) self.assertEqual('failure' if fail_on_errors else 'success', actual)
def parse_junit_xml_files(files: Iterable[str]) -> ParsedUnitTestResults: """Parses junit xml files and returns aggregated statistics as a ParsedUnitTestResults.""" def parse(path: str) -> Union[str, Any]: if not os.path.exists(path): return FileNotFoundError(f'File does not exist.') if os.stat(path).st_size == 0: return Exception(f'File is empty.') try: return JUnitXml.fromfile(path) except BaseException as e: return e parsed_files = [(result_file, parse(result_file)) for result_file in files] junits = [(result_file, junit) for result_file, junit in parsed_files if not isinstance(junit, BaseException)] errors = [ ParseError.from_exception(result_file, exception) for result_file, exception in parsed_files if isinstance(exception, BaseException) ] suites = [(result_file, suite) for result_file, junit in junits for suite in (junit if junit._tag == "testsuites" else [junit])] suite_tests = sum([suite.tests for result_file, suite in suites]) suite_skipped = sum( [suite.skipped + suite.disabled for result_file, suite in suites]) suite_failures = sum([suite.failures for result_file, suite in suites]) suite_errors = sum([suite.errors for result_file, suite in suites]) suite_time = int(sum([suite.time for result_file, suite in suites])) def int_opt(string: Optional[str]) -> Optional[int]: try: return int(string) if string else None except ValueError: return None def get_cases(suite: TestSuite) -> List[TestCase]: """ JUnit seems to allow for testsuite tags inside testsuite tags, potentially at any depth. https://llg.cubic.org/docs/junit/ This skips all inner testsuite tags and returns a list of all contained testcase tags. """ suites = list(suite.iterchildren(TestSuite)) cases = list(suite.iterchildren(TestCase)) return [case for suite in suites for case in get_cases(suite)] + cases cases = [ UnitTestCase(result_file=result_file, test_file=case._elem.get('file'), line=int_opt(case._elem.get('line')), class_name=case.classname, test_name=case.name, result=get_result(results), message=get_message(results), content=get_content(results), time=case.time) for result_file, suite in suites for case in get_cases(suite) if case.classname is not None or case.name is not None for results in [get_results(case.result, case.status)] ] return ParsedUnitTestResults( files=len(parsed_files), errors=errors, # test state counts from suites suites=len(suites), suite_tests=suite_tests, suite_skipped=suite_skipped, suite_failures=suite_failures, suite_errors=suite_errors, suite_time=suite_time, # test cases cases=cases)
def test_get_stats_delta(self): self.assertEqual(get_stats_delta(UnitTestRunResults( files=1, errors=errors, suites=2, duration=3, tests=20, tests_succ=2, tests_skip=5, tests_fail=6, tests_error=7, runs=40, runs_succ=12, runs_skip=8, runs_fail=9, runs_error=10, commit='commit' ), UnitTestRunResults( files=3, errors=[ParseError('other file', 'other error', None, None)], suites=5, duration=7, tests=41, tests_succ=5, tests_skip=11, tests_fail=13, tests_error=15, runs=81, runs_succ=25, runs_skip=17, runs_fail=19, runs_error=21, commit='ref' ), 'type'), UnitTestRunDeltaResults( files=n(1, -2), errors=errors, suites=n(2, -3), duration=d(3, -4), tests=n(20, -21), tests_succ=n(2, -3), tests_skip=n(5, -6), tests_fail=n(6, -7), tests_error=n(7, -8), runs=n(40, -41), runs_succ=n(12, -13), runs_skip=n(8, -9), runs_fail=n(9, -10), runs_error=n(10, -11), commit='commit', reference_commit='ref', reference_type='type' ))
def test_parse_error_from_error(self): actual = ParseError.from_exception('file', ValueError('error')) expected = ParseError('file', 'error', None, None) self.assertEqual(expected, actual)
def test_parse_error_from_file_not_found(self): error = FileNotFoundError(2, 'No such file or directory') error.filename = 'some file path' actual = ParseError.from_exception('file', error) expected = ParseError('file', "[Errno 2] No such file or directory: 'some file path'", None, None) self.assertEqual(expected, actual)
import unittest from xml.etree.ElementTree import ParseError as XmlParseError from publish.unittestresults import get_test_results, get_stats, get_stats_delta, \ ParsedUnitTestResults, ParsedUnitTestResultsWithCommit, \ UnitTestCase, UnitTestResults, UnitTestCaseResults, \ UnitTestRunResults, UnitTestRunDeltaResults, ParseError from test import d, n errors = [ParseError('file', 'error', None, None)] class TestUnitTestResults(unittest.TestCase): def test_parse_error_from_xml_parse_error(self): error = XmlParseError('xml parse error') error.code = 123 error.position = (1, 2) actual = ParseError.from_exception('file', error) expected = ParseError('file', 'xml parse error', 1, 2) self.assertEqual(expected, actual) def test_parse_error_from_file_not_found(self): error = FileNotFoundError(2, 'No such file or directory') error.filename = 'some file path' actual = ParseError.from_exception('file', error) expected = ParseError('file', "[Errno 2] No such file or directory: 'some file path'", None, None) self.assertEqual(expected, actual) def test_parse_error_from_error(self): actual = ParseError.from_exception('file', ValueError('error'))