Skip to content

Commit 65e6d89

Browse files
committed
fix(test): move #1389 measurement hooks to root e2e conftest
The prior fold placed the ``pytest_sessionfinish`` / ``pytest_testnodedown`` / ``pytest_terminal_summary`` hooks at ``tests/src/e2e/workflows/automation/conftest.py``. CI re-ran cleanly, test passed, but the measurement section still didn't surface — the xdist master only loads conftests up to where tests are collected, and ``pytest_terminal_summary`` (which writes to the visible terminal) is a master-only hook. Deep subdir conftests don't get their hooks invoked on the master. The Readiness gate timings pattern works because its hooks live at ``tests/src/e2e/conftest.py`` (the root e2e conftest the master always loads). Same fix here: move the storage, the ``record_poll_cadence_measurement`` helper, and the three hook extensions to the root conftest alongside the readiness ones. The deep ``workflows/automation/conftest.py`` is removed; the measurement test imports the helper from the root.
1 parent fba9dbd commit 65e6d89

3 files changed

Lines changed: 44 additions & 85 deletions

File tree

tests/src/e2e/conftest.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@
8989
_READINESS_TIMINGS: list[dict[str, Any]] = []
9090
_ALL_READINESS_TIMINGS: list[dict[str, Any]] = []
9191

92+
# Same shape as ``_READINESS_TIMINGS``, parallel channel for the #1389
93+
# ``_POLL_CADENCE`` measurement. Lives at this conftest level (not deeper)
94+
# because pytest-xdist's master only loads conftests up to where tests
95+
# are collected; deeper subdir conftests don't get their hooks invoked
96+
# on the master, which is where ``pytest_terminal_summary`` writes to
97+
# the visible terminal output. See ``record_poll_cadence_measurement``
98+
# call site in ``workflows/automation/test_poll_cadence_measurement.py``.
99+
_POLL_CADENCE_MEASUREMENTS: list[dict[str, Any]] = []
100+
_ALL_POLL_CADENCE_MEASUREMENTS: list[dict[str, Any]] = []
101+
102+
103+
def record_poll_cadence_measurement(measurement: dict[str, Any]) -> None:
104+
"""Record a single #1389 measurement run from a test method.
105+
106+
Expected keys: ``n``, ``attempts``, ``p50``, ``p90``, ``p99``, ``min``,
107+
``max``, ``not_verified``, ``verdict``, ``samples``.
108+
"""
109+
_POLL_CADENCE_MEASUREMENTS.append(measurement)
110+
92111

93112
def _log_readiness_timing(gate: str, elapsed_s: float, **extras: Any) -> None:
94113
"""Record a fixture-side readiness-gate timing data point.
@@ -178,6 +197,8 @@ def pytest_sessionfinish(session, exitstatus):
178197
workeroutput = getattr(session.config, "workeroutput", None)
179198
if workeroutput is not None and _READINESS_TIMINGS:
180199
workeroutput["readiness_timings"] = list(_READINESS_TIMINGS)
200+
if workeroutput is not None and _POLL_CADENCE_MEASUREMENTS:
201+
workeroutput["poll_cadence_measurements"] = list(_POLL_CADENCE_MEASUREMENTS)
181202

182203

183204
def pytest_testnodedown(node, error):
@@ -187,8 +208,11 @@ def pytest_testnodedown(node, error):
187208
the worker crashed — we still try to drain whatever it managed to
188209
record.
189210
"""
190-
timings = getattr(node, "workeroutput", {}).get("readiness_timings", [])
191-
_ALL_READINESS_TIMINGS.extend(timings)
211+
workeroutput = getattr(node, "workeroutput", {})
212+
_ALL_READINESS_TIMINGS.extend(workeroutput.get("readiness_timings", []))
213+
_ALL_POLL_CADENCE_MEASUREMENTS.extend(
214+
workeroutput.get("poll_cadence_measurements", [])
215+
)
192216

193217

194218
def pytest_terminal_summary(terminalreporter, exitstatus, config):
@@ -231,6 +255,23 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config):
231255
f"follow-up gate justified."
232256
)
233257

258+
# #1389 _POLL_CADENCE measurement section — same xdist-aware plumbing
259+
# as the readiness gates above. Falls back to the local list when running
260+
# without xdist (no ``pytest_testnodedown`` fires in that mode).
261+
poll_measurements = _ALL_POLL_CADENCE_MEASUREMENTS or _POLL_CADENCE_MEASUREMENTS
262+
if poll_measurements:
263+
terminalreporter.section("#1389 _POLL_CADENCE measurement")
264+
for m in poll_measurements:
265+
terminalreporter.write_line(
266+
f"[POLL_CADENCE_1389] N={m['n']}/{m.get('attempts', m['n'])} "
267+
f"p50={m['p50']:.1f}ms p90={m['p90']:.1f}ms p99={m['p99']:.1f}ms "
268+
f"min={m['min']:.1f}ms max={m['max']:.1f}ms "
269+
f"not_verified={m['not_verified']} VERDICT={m['verdict']}"
270+
)
271+
terminalreporter.write_line(
272+
f"[POLL_CADENCE_1389] samples_ms={m['samples']}"
273+
)
274+
234275

235276
def _is_missing_column_or_table_error(exc: sqlite3.OperationalError) -> bool:
236277
"""Return True only for benign 'schema drift' errors (column/table missing).

tests/src/e2e/workflows/automation/conftest.py

Lines changed: 0 additions & 82 deletions
This file was deleted.

tests/src/e2e/workflows/automation/test_poll_cadence_measurement.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
import pytest
2525

26+
from ...conftest import record_poll_cadence_measurement
2627
from ...utilities.assertions import safe_call_tool
27-
from .conftest import record_poll_cadence_measurement
2828

2929
logger = logging.getLogger(__name__)
3030

0 commit comments

Comments
 (0)