tractor/tests
Gud Boi eceed29d4a Pin forkserver hang to pytest `--capture=fd`
Sixth and final diagnostic pass — after all 4
cascade fixes landed (FD hygiene, pidfd wait,
`_parent_chan_cs` wiring, bounded peer-clear), the
actual last gate on
`test_nested_multierrors[subint_forkserver]`
turned out to be **pytest's default
`--capture=fd` stdout/stderr capture**, not
anything in the runtime cascade.

Empirical result: `pytest -s` → test PASSES in
6.20s. Default `--capture=fd` → hangs forever.

Mechanism: pytest replaces the parent's fds 1,2
with pipe write-ends it reads from. Fork children
inherit those pipes (since `_close_inherited_fds`
correctly preserves stdio). The error-propagation
cascade in a multi-level cancel test generates
7+ actors each logging multiple `RemoteActorError`
/ `ExceptionGroup` tracebacks — enough output to
fill Linux's 64KB pipe buffer. Writes block,
subactors can't progress, processes don't exit,
`_ForkedProc.wait` hangs.

Self-critical aside: I earlier tested w/ and w/o
`-s` and both hung, concluding "capture-pipe
ruled out". That was wrong — at that time fixes
1-4 weren't all in place, so the test was
failing at deeper levels long before reaching
the "produce lots of output" phase. Once the
cascade could actually tear down cleanly, enough
output flowed to hit the pipe limit. Order-of-
operations mistake: ruling something out based
on a test that was failing for a different
reason.

Deats,
- `subint_forkserver_test_cancellation_leak_issue
  .md`: new section "Update — VERY late: pytest
  capture pipe IS the final gate" w/ DIAG timeline
  showing `trio.run` fully returns, diagnosis of
  pipe-fill mechanism, retrospective on the
  earlier wrong ruling-out, and fix direction
  (redirect subactor stdout/stderr to `/dev/null`
  in fork-child prelude, conditional on
  pytest-detection or opt-in flag)
- `tests/test_cancellation.py`: skip-mark reason
  rewritten to describe the capture-pipe gate
  specifically; cross-refs the new doc section
- `tests/spawn/test_subint_forkserver.py`: the
  orphan-SIGINT test regresses back to xfail.
  Previously passed after the FD-hygiene fix,
  but the new `wait_for_no_more_peers(
  move_on_after=3.0)` bound in `async_main`'s
  teardown added up to 3s latency, pushing
  orphan-subactor exit past the test's 10s poll
  window. Real fix: faster orphan-side teardown
  OR extend poll window to 15s

No runtime code changes in this commit — just
test-mark adjustments + doc wrap-up.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-23 23:18:14 -04:00
..
devx Mark `subint`-hanging tests with `skipon_spawn_backend` 2026-04-23 18:47:49 -04:00
discovery Mark `subint`-hanging tests with `skipon_spawn_backend` 2026-04-23 18:47:49 -04:00
ipc Update tests+examples imports for new subpkgs 2026-04-02 17:59:13 -04:00
msg Update tests+examples imports for new subpkgs 2026-04-02 17:59:13 -04:00
spawn Pin forkserver hang to pytest `--capture=fd` 2026-04-23 23:18:14 -04:00
__init__.py Add `tests/__init__.py` for `.conftest` imports 2025-03-20 20:53:54 -04:00
conftest.py Mark `subint`-hanging tests with `skipon_spawn_backend` 2026-04-23 18:47:49 -04:00
test_2way.py Tidy a typing-typo, add explicit `ids=` for paramed suites 2026-03-09 19:35:47 -04:00
test_advanced_faults.py Revert advanced-fault UDS edge case handling 2026-03-13 21:10:52 -04:00
test_advanced_streaming.py Remove lingering seg=False-flags from tests 2025-08-18 12:03:32 -04:00
test_cancellation.py Pin forkserver hang to pytest `--capture=fd` 2026-04-23 23:18:14 -04:00
test_child_manages_service_nursery.py Swap `open_channel_from()` to yield `(chan, first)` 2026-03-13 19:28:57 -04:00
test_clustering.py Skip `test_empty_mngrs_input_raises` on UDS tpt 2026-04-02 17:59:13 -04:00
test_context_stream_semantics.py Update tests+examples imports for new subpkgs 2026-04-02 17:59:13 -04:00
test_docs_examples.py Move `get_cpu_state()` to `conftest` as shared latency headroom 2026-04-02 17:59:13 -04:00
test_infected_asyncio.py Update tests+examples imports for new subpkgs 2026-04-02 17:59:13 -04:00
test_inter_peer_cancellation.py Mark `subint`-hanging tests with `skipon_spawn_backend` 2026-04-23 18:47:49 -04:00
test_legacy_one_way_streaming.py Move `get_cpu_state()` to `conftest` as shared latency headroom 2026-04-02 17:59:13 -04:00
test_local.py Rename `Arbiter` -> `Registrar`, mv to `discovery._registry` 2026-04-02 17:59:13 -04:00
test_log_sys.py Mk `test_implicit_mod_name_applied_for_child()` check init-mods 2026-02-11 21:43:37 -05:00
test_multi_program.py Rename `discovery._discovery` to `._api` 2026-04-14 19:54:14 -04:00
test_oob_cancellation.py Woops, fix missing `assert` thanks to copilot 2025-09-11 13:13:18 -04:00
test_pubsub.py Mark `subint`-hanging tests with `skipon_spawn_backend` 2026-04-23 18:47:49 -04:00
test_reg_err_types.py Drop stale `.cancel()`, fix docstring typo in tests 2026-04-02 18:21:19 -04:00
test_remote_exc_relay.py Adjust ep-masking-suite for the real-use-case 2025-07-15 07:23:21 -04:00
test_resource_cache.py Scale `test_open_local_sub_to_stream` timeout by CPU factor 2026-04-16 20:03:32 -04:00
test_ringbuf.py Avoid skip `.ipc._ringbuf` import when no `cffi` 2026-04-23 18:47:49 -04:00
test_root_infect_asyncio.py Swap `open_channel_from()` to yield `(chan, first)` 2026-03-13 19:28:57 -04:00
test_root_runtime.py Update tests+examples imports for new subpkgs 2026-04-02 17:59:13 -04:00
test_rpc.py Rename `Arbiter` -> `Registrar`, mv to `discovery._registry` 2026-04-02 17:59:13 -04:00
test_runtime.py Repair lifetime-stack suite's flakiness 2026-03-13 21:10:52 -04:00
test_shm.py Mark `subint`-hanging tests with `skipon_spawn_backend` 2026-04-23 18:47:49 -04:00
test_spawning.py Tweak timeouts and rm `arbiter_addr` in tests 2026-04-14 19:54:14 -04:00
test_task_broadcasting.py Tweak timeouts and rm `arbiter_addr` in tests 2026-04-14 19:54:14 -04:00
test_trioisms.py Tweaks from copilot, type fix, typos, language. 2025-09-11 10:01:25 -04:00