| #!/usr/bin/env python3 |
| |
| # Turns a Meson testlog.json file into a JUnit XML report |
| # |
| # Copyright 2019 GNOME Foundation |
| # |
| # SPDX-License-Identifier: LGPL-2.1-or-later |
| # |
| # Original author: Emmanuele Bassi |
| |
| import argparse |
| import datetime |
| import json |
| import sys |
| import xml.etree.ElementTree as ET |
| |
| aparser = argparse.ArgumentParser( |
| description="Turns a Meson test log into a JUnit report" |
| ) |
| aparser.add_argument( |
| "--project-name", metavar="NAME", help="The project name", default="unknown" |
| ) |
| aparser.add_argument( |
| "--job-id", metavar="ID", help="The job ID for the report", default="Unknown" |
| ) |
| aparser.add_argument( |
| "--branch", |
| metavar="NAME", |
| help="Branch of the project being tested", |
| default="main", |
| ) |
| aparser.add_argument( |
| "--output", |
| metavar="FILE", |
| help="The output file, stdout by default", |
| type=argparse.FileType("w", encoding="UTF-8"), |
| default=sys.stdout, |
| ) |
| aparser.add_argument( |
| "infile", |
| metavar="FILE", |
| help="The input testlog.json, stdin by default", |
| type=argparse.FileType("r", encoding="UTF-8"), |
| default=sys.stdin, |
| ) |
| |
| args = aparser.parse_args() |
| |
| outfile = args.output |
| |
| testsuites = ET.Element("testsuites") |
| testsuites.set("id", "{}/{}".format(args.job_id, args.branch)) |
| testsuites.set("package", args.project_name) |
| testsuites.set("timestamp", datetime.datetime.utcnow().isoformat()) |
| |
| suites = {} |
| for line in args.infile: |
| data = json.loads(line) |
| (full_suite, unit_name) = data["name"].split(" / ") |
| try: |
| (project_name, suite_name) = full_suite.split(":") |
| except ValueError: |
| project_name = full_suite |
| suite_name = full_suite |
| |
| duration = data["duration"] |
| return_code = data["returncode"] |
| log = data["stdout"] |
| log_stderr = data.get("stderr", "") |
| |
| unit = { |
| "suite": suite_name, |
| "name": unit_name, |
| "duration": duration, |
| "returncode": return_code, |
| "stdout": log, |
| "stderr": log_stderr, |
| } |
| |
| units = suites.setdefault(suite_name, []) |
| units.append(unit) |
| |
| for name, units in suites.items(): |
| print("Processing suite {} (units: {})".format(name, len(units))) |
| |
| def if_failed(unit): |
| if unit["returncode"] != 0: |
| return True |
| return False |
| |
| def if_succeded(unit): |
| if unit["returncode"] == 0: |
| return True |
| return False |
| |
| successes = list(filter(if_succeded, units)) |
| failures = list(filter(if_failed, units)) |
| print(" - {}: {} pass, {} fail".format(name, len(successes), len(failures))) |
| |
| testsuite = ET.SubElement(testsuites, "testsuite") |
| testsuite.set("name", "{}/{}".format(args.project_name, name)) |
| testsuite.set("tests", str(len(units))) |
| testsuite.set("errors", str(len(failures))) |
| testsuite.set("failures", str(len(failures))) |
| |
| for unit in successes: |
| testcase = ET.SubElement(testsuite, "testcase") |
| testcase.set("classname", "{}/{}".format(args.project_name, unit["suite"])) |
| testcase.set("name", unit["name"]) |
| testcase.set("time", str(unit["duration"])) |
| |
| for unit in failures: |
| testcase = ET.SubElement(testsuite, "testcase") |
| testcase.set("classname", "{}/{}".format(args.project_name, unit["suite"])) |
| testcase.set("name", unit["name"]) |
| testcase.set("time", str(unit["duration"])) |
| |
| failure = ET.SubElement(testcase, "failure") |
| failure.set("classname", "{}/{}".format(args.project_name, unit["suite"])) |
| failure.set("name", unit["name"]) |
| failure.set("type", "error") |
| failure.text = unit["stdout"] + "\n" + unit["stderr"] |
| |
| output = ET.tostring(testsuites, encoding="unicode") |
| outfile.write(output) |