diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5cfaf6e6e06..b3fe25c8672 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -753,83 +753,16 @@ Commit Check: # https://docs.gitlab.com/ee/ci/testing/unit_test_reports.html # https://www.ibm.com/docs/en/developer-for-zos/14.2?topic=formats-junit-xml-format # - # Let's generate a unit test report, even if that means wondering which life choices - # led us to assemlbing an XML document from fragments in a shell script which is - # embedded in YAML. - - FAILURE_COUNT=0 - - TESTCASES="" - FAILURE_COUNT=$(( FAILURE_COUNT + 1 )) - else - TESTCASES+="/>" - fi - - PC_EXIT_CODE=0 - - ANALYSIS_MESSAGE=$( ./tools/pre-commit "${CI_COMMIT_SHA}~$NUM_COMMITS" ) || PC_EXIT_CODE=1 - - TESTCASES+=" commit_checks.xml < - - $TESTCASES - - FIN - # - cat commit_checks.xml - - grep ' 0) or (args.command is None and len(command_list) == 0): + sys.stderr.write('Error: The command must be provided via the --command flag or extra arguments.\n') + sys.exit(1) + + try: + tree = ET.parse(args.file) + testsuites_el = tree.getroot() + except FileNotFoundError: + testsuites_el = ET.Element('testsuites') + tree = ET.ElementTree(testsuites_el) + except ET.ParseError: + sys.stderr.write(f'Error: {args.file} is invalid.\n') + sys.exit(1) + + suites_time = float(testsuites_el.get('time', 0.0)) + suites_tests = int(testsuites_el.get('tests', 0)) + 1 + suites_failures = int(testsuites_el.get('failures', 0)) + + testsuite_el = testsuites_el.find(f'./testsuite[@name="{args.suite}"]') + if testsuite_el is None: + testsuite_el = ET.Element('testsuite', attrib={'name': args.suite}) + testsuites_el.append(testsuite_el) + + suite_time = float(testsuite_el.get('time', 0.0)) + suite_tests = int(testsuite_el.get('tests', 0)) + 1 + suite_failures = int(testsuite_el.get('failures', 0)) + + testcase_el = ET.Element('testcase', attrib={'name': args.case}) + testsuite_el.append(testcase_el) + + if args.command: + proc_args = args.command + in_shell = True + else: + proc_args = command_list + in_shell = False + + start_time = time.perf_counter() + proc = subprocess.run(proc_args, shell=in_shell, encoding='UTF-8', errors='replace', capture_output=True) + case_time = time.perf_counter() - start_time + + testcase_el.set('time', f'{case_time}') + testsuite_el.set('time', f'{suite_time + case_time}') + testsuites_el.set('time', f'{suites_time + case_time}') + + # XXX Try to interleave them? + sys.stdout.write(proc.stdout) + sys.stderr.write(proc.stderr) + + # Remove ANSI control sequences and escape other invalid characters + # https://stackoverflow.com/a/14693789/82195 + ansi_seq_re = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + scrubbed_stdout = html.escape(ansi_seq_re.sub('', proc.stdout), quote=False) + scrubbed_stderr = html.escape(ansi_seq_re.sub('', proc.stderr), quote=False) + + if proc.returncode != 0: + failure_el = ET.Element('failure') + failure_el.text = f'{scrubbed_stdout}{scrubbed_stderr}' + testcase_el.append(failure_el) + testsuite_el.set('failures', f'{suite_failures + 1}') + testsuites_el.set('failures', f'{suites_failures + 1}') + else: + system_out_el = ET.Element('system-out') + system_out_el.text = f'{scrubbed_stdout}' + testcase_el.append(system_out_el) + system_err_el = ET.Element('system-err') + system_err_el.text = f'{scrubbed_stderr}' + testcase_el.append(system_err_el) + + testsuite_el.set('tests', f'{suite_tests}') + testsuites_el.set('tests', f'{suites_tests}') + + tree.write(args.file, encoding='UTF-8', xml_declaration=True) + + return proc.returncode + +if __name__ == '__main__': + sys.exit(main())