test: infamy: always emit the "1..N" plan line before exiting

Always emit the "1..N" plan line before exiting with error so test
harnesses don't report "test error, no plan" for failed or aborted
tests.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-03-09 19:25:29 +01:00
parent 9ea1e82728
commit 982e610fef
+32 -1
View File
@@ -1,3 +1,32 @@
"""TAP (Test Anything Protocol) output helpers for Infamy tests.
TAP is a simple line-based protocol for communicating test results between a
test producer and a consumer (harness). The canonical reference is:
https://testanything.org/tap-specification.html
The key elements are:
Plan line: ``1..N`` — total number of tests, written once
Result lines: ``ok N - desc`` — test N passed
``not ok N - desc``— test N failed
Directives: ``ok N # SKIP`` ``not ok N # TODO``
Diagnostics: Any line beginning with ``#`` is a comment/diagnostic.
The plan line may appear either at the beginning (before any results) or at
the end (after all results). Infamy writes it at the END so that the total
step count is known; a harness that requires an upfront plan will report
"no plan" on failure — see CommentWriter below for a known limitation.
``CommentWriter`` redirects Python's ``sys.stdout`` so that ordinary
``print()`` calls are prefixed with ``# `` and appear as diagnostics, while
TAP result lines (written via ``self.out``, the *original* stdout) are emitted
verbatim. The plan line is also written via ``self.out``.
Note: because ``sys.stdout`` is replaced *after* module import the default
argument ``output=sys.stdout`` captures the original stream at import time,
so nested or re-entrant ``Test()`` instances will share the same ``self.out``.
"""
import contextlib
import datetime
import subprocess
@@ -41,7 +70,7 @@ class Test:
self._not_ok("Missing explicit test result\n")
else:
if t in (TestPass, TestSkip):
self.out.write(f"{self.steps}..{self.steps}\n")
self.out.write(f"1..{self.steps}\n")
self.out.flush()
raise SystemExit(0)
if t is AssertionError:
@@ -56,6 +85,8 @@ class Test:
elif len(e.args) and type(e.args[0]) is subprocess.CompletedProcess:
print("Failing subprocess stdout:\n", e.args[0].stdout)
self.out.write(f"1..{self.steps}\n")
self.out.flush()
raise SystemExit(1)
@contextlib.contextmanager