#!/usr/bin/env python3 """Convert Surefire/JUnit XML (eunit_surefire) into a simple HTML report.""" from __future__ import annotations import argparse import html import sys import xml.etree.ElementTree as ET from pathlib import Path def load_suites(paths: list[Path]) -> list[ET.Element]: suites: list[ET.Element] = [] for path in paths: try: root = ET.parse(path).getroot() except ET.ParseError as exc: print(f"skip {path}: {exc}", file=sys.stderr) continue if root.tag == "testsuite": suites.append(root) elif root.tag == "testsuites": suites.extend(list(root.findall("testsuite"))) return suites def render(suites: list[ET.Element], title: str) -> str: total = failures = errors = skipped = 0 rows: list[str] = [] for suite in suites: s_name = suite.attrib.get("name", "suite") total += int(suite.attrib.get("tests", "0") or 0) failures += int(suite.attrib.get("failures", "0") or 0) errors += int(suite.attrib.get("errors", "0") or 0) skipped += int(suite.attrib.get("skipped", "0") or 0) for case in suite.findall("testcase"): name = case.attrib.get("name", "?") classname = case.attrib.get("classname", s_name) time_s = case.attrib.get("time", "") fail = case.find("failure") err = case.find("error") skip = case.find("skipped") if fail is not None: status, detail = "FAIL", fail.attrib.get("message") or (fail.text or "") elif err is not None: status, detail = "ERROR", err.attrib.get("message") or (err.text or "") elif skip is not None: status, detail = "SKIP", skip.attrib.get("message") or "" else: status, detail = "PASS", "" cls = status.lower() rows.append( "
{detail}tests: {total} pass: {ok} fail: {failures} error: {errors} skip: {skipped}
| Status | Suite | Case | Time | Detail |
|---|---|---|---|---|
| No testcases | ||||