mirror of
https://github.com/tbamud/tbamud.git
synced 2026-04-30 04:41:51 +02:00
Agent-Logs-Url: https://github.com/tbamud/tbamud/sessions/8d948e86-1d59-496f-a317-7fd9294fcad8 Co-authored-by: welcor <357770+welcor@users.noreply.github.com>
81 lines
2.6 KiB
Python
81 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Convert Unity test-runner output to JUnit XML.
|
|
|
|
Usage:
|
|
./test_binary | python3 unity_to_junit.py <suite_name> <output.xml> [elapsed_seconds]
|
|
|
|
Unity emits one result line per test:
|
|
path/to/file.c:LINE:TEST_NAME:PASS
|
|
path/to/file.c:LINE:TEST_NAME:FAIL:message
|
|
path/to/file.c:LINE:TEST_NAME:IGNORE:message
|
|
followed by a summary line:
|
|
N Tests N Failures N Ignored
|
|
"""
|
|
|
|
import re
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
def parse_unity(lines):
|
|
tests = []
|
|
total = failures = ignored = 0
|
|
for line in lines:
|
|
line = line.rstrip("\n")
|
|
m = re.match(
|
|
r"^.+:\d+:([^:]+):(PASS|FAIL|IGNORE)(?::(.*))?$", line
|
|
)
|
|
if m:
|
|
name, result, message = m.group(1), m.group(2), m.group(3) or ""
|
|
tests.append((name, result, message))
|
|
continue
|
|
m2 = re.match(r"^(\d+) Tests (\d+) Failures (\d+) Ignored", line)
|
|
if m2:
|
|
total, failures, ignored = int(m2.group(1)), int(m2.group(2)), int(m2.group(3))
|
|
if not total:
|
|
total = len(tests)
|
|
failures = sum(1 for _, r, _ in tests if r == "FAIL")
|
|
ignored = sum(1 for _, r, _ in tests if r == "IGNORE")
|
|
return tests, total, failures, ignored
|
|
|
|
|
|
def build_xml(suite_name, tests, total, failures, ignored, elapsed):
|
|
# Distribute total time evenly across tests for per-testcase timing.
|
|
per_test = round(elapsed / total, 6) if total else 0.0
|
|
suite = ET.Element(
|
|
"testsuite",
|
|
name=suite_name,
|
|
tests=str(total),
|
|
failures=str(failures),
|
|
errors="0",
|
|
skipped=str(ignored),
|
|
time=f"{elapsed:.6f}",
|
|
)
|
|
for name, result, message in tests:
|
|
case = ET.SubElement(
|
|
suite, "testcase",
|
|
name=name, classname=suite_name, time=f"{per_test:.6f}",
|
|
)
|
|
if result == "FAIL":
|
|
f = ET.SubElement(case, "failure", message=message)
|
|
f.text = message
|
|
elif result == "IGNORE":
|
|
ET.SubElement(case, "skipped", message=message)
|
|
return ET.ElementTree(suite)
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 3 or len(sys.argv) > 4:
|
|
print(f"usage: {sys.argv[0]} <suite_name> <output.xml> [elapsed_seconds]", file=sys.stderr)
|
|
sys.exit(1)
|
|
suite_name, output_file = sys.argv[1], sys.argv[2]
|
|
elapsed = float(sys.argv[3]) if len(sys.argv) == 4 else 0.0
|
|
tests, total, failures, ignored = parse_unity(sys.stdin.readlines())
|
|
tree = build_xml(suite_name, tests, total, failures, ignored, elapsed)
|
|
ET.indent(tree, space=" ")
|
|
tree.write(output_file, encoding="unicode", xml_declaration=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|