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 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_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)