The Gaudi Framework  master (ff829712)
Loading...
Searching...
No Matches
ctest_measurements_reporter.py
Go to the documentation of this file.
11import os
12import re
13import xml.sax.saxutils as XSS
14from collections import defaultdict
15
16# This plugin integrates with pytest to capture and report test results
17# in a format compatible with CTest using DartMeasurement tags
18# Key functions and hooks include:
19# - sanitize_for_xml: Sanitize a string by escaping non-XML characters.
20# - pytest_report_header: Add strings to the pytest header.
21# - pytest_runtest_logreport: Collect test results and durations.
22# - pytest_sessionfinish: Output the collected test information in a format
23# suitable for CTest.
24
25results = {}
26
27
29 bad_chars = re.compile("[\x00-\x08\x0b\x0c\x0e-\x1f\ud800-\udfff\ufffe\uffff]")
30
31 def quote(match):
32 "helper function"
33 return "".join("[NON-XML-CHAR-0x%2X]" % ord(c) for c in match.group())
34
35 return bad_chars.sub(quote, data)
36
37
38def pytest_report_header(config, start_path, startdir):
39 # make sure CTest does not drop output lines on successful tests
40 return "CTEST_FULL_OUTPUT"
41
42
44 # collect user properties
45 head_line = report.head_line
46 results.update(
47 {
48 f"{head_line}.{k}": v
49 for k, v in report.user_properties
50 if f"{head_line}.{k}" not in results
51 }
52 )
53
54 # collect test outcome
55 if not report.passed:
56 results[f"{report.head_line}.outcome"] = report.outcome
57 else:
58 results.setdefault(f"{report.head_line}.outcome", "passed")
59
60 # collect test duration
61 if report.when == "call":
62 results[f"{report.head_line}.duration"] = round(report.duration, 2)
63
64
65def pytest_sessionfinish(session, exitstatus):
66 if not hasattr(session, "items"):
67 # no test run, nothing to report
68 return
69 if os.environ.get("DISABLE_CTEST_MEASUREMENTS") == "1":
70 # user requested to disable CTest measurements printouts
71 return
72
73 outcomes = defaultdict(list)
74 for key in results:
75 if key.endswith(".outcome"):
76 outcomes[results[key]].append(key[:-8])
77 results.update(
78 (f"outcome.{outcome}", sorted(tests)) for outcome, tests in outcomes.items()
79 )
80
81 ignore_keys = {"test_fixture_setup.completed_process"}
82 template = (
83 '<DartMeasurement type="text/string" name="{name}">{value}</DartMeasurement>'
84 )
85
86 to_print = [
87 (key, value)
88 for key, value in results.items()
89 if not any(key.endswith(ignore_key) for ignore_key in ignore_keys) and value
90 ]
91 to_print.sort()
92 for key, value in to_print:
93 sanitized_value = XSS.escape(sanitize_for_xml(str(value)))
94 # workaround for a limitation of CTestXML2HTML
95 key = key.replace("/", "_")
96 print(template.format(name=key, value=sanitized_value), end="")
pytest_report_header(config, start_path, startdir)