Skip to content

Commit e8fd6ac

Browse files
Copilotigaw
authored andcommitted
tests: ensure TAP output and test output separate
The tests print diagnostic output which mingles with TAP. Thus redirect/buffer the output from the tests and sync the output with TAP outputs. As result, Meson's test framework understands the status report from the tests. Signed-off-by: Daniel Wagner <[email protected]>
1 parent 25416a4 commit e8fd6ac

1 file changed

Lines changed: 43 additions & 8 deletions

File tree

tests/tap_runner.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,34 @@
1414

1515
import argparse
1616
import importlib
17+
import io
1718
import sys
1819
import traceback
1920
import unittest
2021

2122

23+
class DiagnosticCapture(io.TextIOBase):
24+
"""Capture writes and re-emit them as TAP diagnostic lines (# ...)."""
25+
26+
def __init__(self, real_stdout: io.TextIOBase) -> None:
27+
self._real = real_stdout
28+
self._buf = ''
29+
30+
def write(self, text: str) -> int:
31+
self._buf += text
32+
while '\n' in self._buf:
33+
line, self._buf = self._buf.split('\n', 1)
34+
self._real.write('# {}\n'.format(line))
35+
self._real.flush()
36+
return len(text)
37+
38+
def flush(self) -> None:
39+
if self._buf:
40+
self._real.write('# {}\n'.format(self._buf))
41+
self._buf = ''
42+
self._real.flush()
43+
44+
2245
class TAPTestResult(unittest.TestResult):
2346
"""Collect unittest results and render them as TAP version 13."""
2447

@@ -72,12 +95,11 @@ def addUnexpectedSuccess(self, test: unittest.TestCase) -> None:
7295
self._lines.append('not ok {} - {} # TODO unexpected success\n'.format(
7396
self._test_count, self._description(test)))
7497

75-
def print_tap(self, stream: object = sys.stdout) -> None:
76-
stream.write('TAP version 13\n') # type: ignore[union-attr]
77-
stream.write('1..{}\n'.format(self._test_count)) # type: ignore[union-attr]
98+
def print_tap(self, stream: io.TextIOBase) -> None:
99+
stream.write('1..{}\n'.format(self._test_count))
78100
for line in self._lines:
79-
stream.write(line) # type: ignore[union-attr]
80-
stream.flush() # type: ignore[union-attr]
101+
stream.write(line)
102+
stream.flush()
81103

82104

83105
def run_tests(test_module_name: str, start_dir: str | None = None) -> bool:
@@ -89,9 +111,22 @@ def run_tests(test_module_name: str, start_dir: str | None = None) -> bool:
89111
loader = unittest.TestLoader()
90112
suite = loader.loadTestsFromModule(module)
91113

92-
result = TAPTestResult()
93-
suite.run(result)
94-
result.print_tap()
114+
real_stdout = sys.stdout
115+
# TAP version header must be the very first line on stdout.
116+
real_stdout.write('TAP version 13\n')
117+
real_stdout.flush()
118+
119+
# Redirect stdout so any print() calls from setUp/tearDown/tests are
120+
# re-emitted as TAP diagnostic lines and do not break the TAP stream.
121+
sys.stdout = DiagnosticCapture(real_stdout) # type: ignore[assignment]
122+
try:
123+
result = TAPTestResult()
124+
suite.run(result)
125+
finally:
126+
sys.stdout.flush()
127+
sys.stdout = real_stdout
128+
129+
result.print_tap(real_stdout)
95130
return result.wasSuccessful()
96131

97132

0 commit comments

Comments
 (0)