1
0
Fork 0
bind9/bin/tests/convert-trs-to-junit.py
Daniel Baumann f66ff7eae6
Adding upstream version 1:9.20.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:32:37 +02:00

148 lines
4.6 KiB
Python
Executable file

#!/usr/bin/env python3
#
# 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()