The Gaudi Framework  v39r0 (5b8b5eda)
ctest_measurements_reporter.py
Go to the documentation of this file.
1 
11 import os
12 import re
13 import xml.sax.saxutils as XSS
14 
15 # This plugin integrates with pytest to capture and report test results
16 # in a format compatible with CTest using DartMeasurement tags
17 
18 # Key functions and hooks include:
19 # - sanitize_for_xml: Sanitizes a string by escaping non-XML characters.
20 # - pytest_configure: Initializes a dictionary to store test suite properties.
21 # - pytest_runtest_makereport: Collects information about failing tests and
22 # exceptions during test execution.
23 # - pytest_sessionfinish: Outputs the collected test information in a format
24 # suitable for CTest.
25 
26 
27 def sanitize_for_xml(data):
28  bad_chars = re.compile("[\x00-\x08\x0b\x0c\x0e-\x1f\ud800-\udfff\ufffe\uffff]")
29 
30  def quote(match):
31  "helper function"
32  return "".join("[NON-XML-CHAR-0x%2X]" % ord(c) for c in match.group())
33 
34  return bad_chars.sub(quote, data)
35 
36 
37 def pytest_sessionstart(session):
38  # list passed, failed and skipped tests
39  session.results = {"pass": set(), "fail": set(), "skip": set()}
40  # make sure CTest does not drop output lines on successful tests
41  print("CTEST_FULL_OUTPUT")
42 
43 
44 def _skipped_item(item):
45  if any(item.iter_markers(name="skip")):
46  return True
47  if any(mark.args[0] for mark in item.iter_markers(name="skipif")):
48  return True
49  return False
50 
51 
52 def pytest_runtest_makereport(item, call):
53  name = (
54  f"{item.cls.__name__}.{item.name}"
55  if hasattr(item, "cls") and item.cls is not None
56  else item.name
57  )
58  result = None
59 
60  if call.when == "setup":
61  if _skipped_item(item):
62  result = "skip"
63  elif call.when == "call":
64  if call.excinfo is not None:
65  if call.excinfo.typename == "Skipped":
66  result = "skip"
67  else:
68  result = "fail"
69  item.user_properties.append(("exception_info", str(call.excinfo.value)))
70  else:
71  result = "pass"
72 
73  if result is not None:
74  item.session.results[result].add(name)
75 
76 
77 def pytest_sessionfinish(session, exitstatus):
78  if not hasattr(session, "items"):
79  # no test run, nothing to report
80  return
81  if os.environ.get("DISABLE_CTEST_MEASUREMENTS") == "1":
82  # user requested to disable CTest measurements printouts
83  return
84 
85  results = list((name, sorted(value)) for name, value in session.results.items())
86 
87  prefix = ""
88  for item in session.items:
89  prefix = (
90  f"{item.cls.__name__}.{item.name}"
91  if hasattr(item, "cls") and item.cls is not None
92  else item.name
93  )
94  for name, value in item.user_properties:
95  results.append((f"{prefix}.{name}", value))
96 
97  if hasattr(session, "sources"):
98  results.extend(
99  (f"{name}.source_code", value) for name, value in session.sources.items()
100  )
101 
102  if hasattr(session, "docstrings"):
103  results.extend(
104  (f"{name}.doc", value) for name, value in session.docstrings.items()
105  )
106 
107  ignore_keys = {"test_fixture_setup.completed_process"}
108  template = (
109  '<DartMeasurement type="text/string" name="{name}">{value}</DartMeasurement>'
110  )
111 
112  to_print = [
113  (key, value)
114  for key, value in results
115  if not any(key.endswith(ignore_key) for ignore_key in ignore_keys) and value
116  ]
117  for key, value in to_print:
118  sanitized_value = XSS.escape(sanitize_for_xml(str(value)))
119  # workaround for a limitation of CTestXML2HTML
120  key = key.replace("/", "_")
121  print(template.format(name=key, value=sanitized_value), end="")
ctest_measurements_reporter.pytest_runtest_makereport
def pytest_runtest_makereport(item, call)
Definition: ctest_measurements_reporter.py:52
ctest_measurements_reporter.pytest_sessionfinish
def pytest_sessionfinish(session, exitstatus)
Definition: ctest_measurements_reporter.py:77
ctest_measurements_reporter.sanitize_for_xml
def sanitize_for_xml(data)
Definition: ctest_measurements_reporter.py:27
ctest_measurements_reporter._skipped_item
def _skipped_item(item)
Definition: ctest_measurements_reporter.py:44
ctest_measurements_reporter.pytest_sessionstart
def pytest_sessionstart(session)
Definition: ctest_measurements_reporter.py:37
GaudiPython.Persistency.add
def add(instance)
Definition: Persistency.py:50