diff options
Diffstat (limited to '')
-rwxr-xr-x | bin/tests/convert-trs-to-junit.py | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/bin/tests/convert-trs-to-junit.py b/bin/tests/convert-trs-to-junit.py new file mode 100755 index 0000000..85b37dd --- /dev/null +++ b/bin/tests/convert-trs-to-junit.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# Convert automake .trs files into JUnit format suitable for Gitlab + +import argparse +import os +import sys +from xml.etree import ElementTree +from xml.etree.ElementTree import Element +from xml.etree.ElementTree import SubElement + + +# getting explicit encoding specification right for Python 2/3 would be messy, +# so let's hope for the best +def read_whole_text(filename): + with open(filename) as inf: # pylint: disable-msg=unspecified-encoding + return inf.read().strip() + + +def read_trs_result(filename): + result = None + with open(filename, "r") as trs: # pylint: disable-msg=unspecified-encoding + for line in trs: + items = line.split() + if len(items) < 2: + raise ValueError("unsupported line in trs file", filename, line) + if items[0] != (":test-result:"): + continue + if result is not None: + raise NotImplementedError("double :test-result:", filename) + result = items[1].upper() + + if result is None: + raise ValueError(":test-result: not found", filename) + + return result + + +def find_test_relative_path(source_dir, in_path): + """Return {in_path}.c if it exists, with fallback to {in_path}""" + candidates_relative = [in_path + ".c", in_path] + for relative in candidates_relative: + absolute = os.path.join(source_dir, relative) + if os.path.exists(absolute): + return relative + raise KeyError + + +def err_out(exception): + raise exception + + +def walk_trss(source_dir): + for cur_dir, _dirs, files in os.walk(source_dir, onerror=err_out): + for filename in files: + if not filename.endswith(".trs"): + continue + + filename_prefix = filename[: -len(".trs")] + log_name = filename_prefix + ".log" + full_trs_path = os.path.join(cur_dir, filename) + full_log_path = os.path.join(cur_dir, log_name) + sub_dir = os.path.relpath(cur_dir, source_dir) + test_name = os.path.join(sub_dir, filename_prefix) + + t = { + "name": test_name, + "full_log_path": full_log_path, + "rel_log_path": os.path.relpath(full_log_path, source_dir), + } + t["result"] = read_trs_result(full_trs_path) + + # try to find dir/file path for a clickable link + try: + t["rel_file_path"] = find_test_relative_path(source_dir, test_name) + except KeyError: + pass # no existing path found + + yield t + + +def append_testcase(testsuite, t): + # attributes taken from + # https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/lib/gitlab/ci/parsers/test/junit.rb + attrs = {"name": t["name"]} + if "rel_file_path" in t: + attrs["file"] = t["rel_file_path"] + + testcase = SubElement(testsuite, "testcase", attrs) + + # Gitlab accepts only [[ATTACHMENT| links for system-out, not raw text + s = SubElement(testcase, "system-out") + s.text = "[[ATTACHMENT|" + t["rel_log_path"] + "]]" + if t["result"].lower() == "pass": + return + + # Gitlab shows output only for failed or skipped tests + if t["result"].lower() == "skip": + err = SubElement(testcase, "skipped") + else: + err = SubElement(testcase, "failure") + err.text = read_whole_text(t["full_log_path"]) + + +def gen_junit(results): + testsuites = Element("testsuites") + testsuite = SubElement(testsuites, "testsuite") + for test in results: + append_testcase(testsuite, test) + return testsuites + + +def check_directory(path): + try: + os.listdir(path) + return path + except OSError as ex: + msg = "Path {} cannot be listed as a directory: {}".format(path, ex) + raise argparse.ArgumentTypeError(msg) + + +def main(): + parser = argparse.ArgumentParser( + description="Recursively search for .trs + .log files and compile " + "them into JUnit XML suitable for Gitlab. Paths in the " + "XML are relative to the specified top directory." + ) + parser.add_argument( + "top_directory", + type=check_directory, + help="root directory where to start scanning for .trs files", + ) + args = parser.parse_args() + junit = gen_junit(walk_trss(args.top_directory)) + + # encode results into file format, on Python 3 it produces bytes + xml = ElementTree.tostring(junit, "utf-8") + # use stdout as a binary file object, Python2/3 compatibility + output = getattr(sys.stdout, "buffer", sys.stdout) + output.write(xml) + + +if __name__ == "__main__": + main() |