Wall-cap `subint` audit tests via `pytest-timeout`
Add a hard process-level wall-clock bound on the two known-hanging subint-backend tests so an unattended suite run can't wedge indefinitely in either of the hang classes doc'd in `ai/conc-anal/`. Deats, - New `testing` dep: `pytest-timeout>=2.3`. - `test_stale_entry_is_deleted`: `@pytest.mark.timeout(3, method='thread')`. The `method='thread'` choice is deliberate — `method='signal'` routes via `SIGALRM` which is starved by the same GIL-hostage path that drops `SIGINT` (see `subint_sigint_starvation_issue.md`), so it'd never actually fire in the starvation case. - `test_subint_non_checkpointing_child`: same decorator, same reasoning (defense-in-depth over the inner `trio.fail_after(15)`). At timeout, `pytest-timeout` hard-kills the pytest process itself — that's the intended behavior here; the alternative is the suite never returning. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codesubint_forkserver_backend
parent
a65fded4c6
commit
189f4e3ffc
|
|
@ -79,6 +79,11 @@ testing = [
|
||||||
# https://docs.pytest.org/en/8.0.x/explanation/goodpractices.html#choosing-a-test-layout-import-rules
|
# https://docs.pytest.org/en/8.0.x/explanation/goodpractices.html#choosing-a-test-layout-import-rules
|
||||||
"pytest>=8.3.5",
|
"pytest>=8.3.5",
|
||||||
"pexpect>=4.9.0,<5",
|
"pexpect>=4.9.0,<5",
|
||||||
|
# per-test wall-clock bound (used via
|
||||||
|
# `@pytest.mark.timeout(..., method='thread')` on the
|
||||||
|
# known-hanging `subint`-backend audit tests; see
|
||||||
|
# `ai/conc-anal/subint_*_issue.md`).
|
||||||
|
"pytest-timeout>=2.3",
|
||||||
]
|
]
|
||||||
repl = [
|
repl = [
|
||||||
"pyperclip>=1.9.0",
|
"pyperclip>=1.9.0",
|
||||||
|
|
|
||||||
|
|
@ -517,6 +517,22 @@ async def kill_transport(
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.parametrize('use_signal', [False, True])
|
# @pytest.mark.parametrize('use_signal', [False, True])
|
||||||
|
#
|
||||||
|
# Wall-clock bound via `pytest-timeout` (`method='thread'`).
|
||||||
|
# Under `--spawn-backend=subint` this test can wedge in an
|
||||||
|
# un-Ctrl-C-able state (abandoned-subint + shared-GIL
|
||||||
|
# starvation → signal-wakeup-fd pipe fills → SIGINT silently
|
||||||
|
# dropped; see `ai/conc-anal/subint_sigint_starvation_issue.md`).
|
||||||
|
# `method='thread'` is specifically required because `signal`-
|
||||||
|
# method SIGALRM suffers the same GIL-starvation path and
|
||||||
|
# wouldn't fire the Python-level handler.
|
||||||
|
# At timeout the plugin hard-kills the pytest process — that's
|
||||||
|
# the intended behavior here; the alternative is an unattended
|
||||||
|
# suite run that never returns.
|
||||||
|
@pytest.mark.timeout(
|
||||||
|
3, # NOTE should be a 2.1s happy path.
|
||||||
|
method='thread',
|
||||||
|
)
|
||||||
def test_stale_entry_is_deleted(
|
def test_stale_entry_is_deleted(
|
||||||
debug_mode: bool,
|
debug_mode: bool,
|
||||||
daemon: subprocess.Popen,
|
daemon: subprocess.Popen,
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,20 @@ def test_subint_happy_teardown(
|
||||||
trio.run(partial(_happy_path, reg_addr, deadline))
|
trio.run(partial(_happy_path, reg_addr, deadline))
|
||||||
|
|
||||||
|
|
||||||
|
# Wall-clock bound via `pytest-timeout` (`method='thread'`)
|
||||||
|
# as defense-in-depth over the inner `trio.fail_after(15)`.
|
||||||
|
# Under the orphaned-channel hang class described in
|
||||||
|
# `ai/conc-anal/subint_cancel_delivery_hang_issue.md`, SIGINT
|
||||||
|
# is still deliverable and this test *should* be unwedgeable
|
||||||
|
# by the inner trio timeout — but sibling subint-backend
|
||||||
|
# tests in this repo have also exhibited the
|
||||||
|
# `subint_sigint_starvation_issue.md` GIL-starvation flavor,
|
||||||
|
# so `method='thread'` keeps us safe in case ordering or
|
||||||
|
# load shifts the failure mode.
|
||||||
|
@pytest.mark.timeout(
|
||||||
|
3, # NOTE never passes pre-3.14+ subints support.
|
||||||
|
method='thread',
|
||||||
|
)
|
||||||
def test_subint_non_checkpointing_child(
|
def test_subint_non_checkpointing_child(
|
||||||
reg_addr: tuple[str, int|str],
|
reg_addr: tuple[str, int|str],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue