Skip to content

test: fix hostile-decoy env race flaking the setup-matrix and e2e suites#135

Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/setup-matrix-env-race
Open

test: fix hostile-decoy env race flaking the setup-matrix and e2e suites#135
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/setup-matrix-env-race

Conversation

@mikolalysenko

Copy link
Copy Markdown
Collaborator

The flake

Seen twice in CI (both times on setup_matrix_pypi's host_guard::poetry_check_recognizes_structural_hook_host): setup --check exits 2 with a clap --help hint instead of 0/1. Passed on rerun with zero code change.

Root cause

Several test binaries deliberately set hostile ambient env decoys process-wide (std::env::set_var("SOCKET_STRICT", "banana"), …) to prove their run() helpers' child-env scrub works — while their sibling tests run on parallel threads in the same process. The scrub snapshots std::env::vars_os() and then spawns; a set_var landing between a sibling's snapshot and its spawn reaches the child un-scrubbed, and SOCKET_STRICT=banana aborts every CLI arg parse with exit 2. The in-code comment claiming the decoys are "safe to set process-wide because every other test scrubs" was exactly the racy assumption.

Mechanism proven RED/GREEN

Made the CI schedule deterministic locally: a 10ms delay before the decoy set (simulating the late thread start CI runners hit) plus a 25ms window between scrub snapshot and spawn → 2/3 host_guard tests fail on every run with the exact CI assertion; with the fix in place and the same probes, 5/5 runs green. (Probes not committed.)

The fix

Applied to every test binary that mutates process env and has parallel siblings (setup_matrix_{pypi,npm,deno,maven,nuget}, e2e_safety_cow, e2e_vendor_pypi_build — found by repo-wide sweep):

  • #[serial_test::serial] on every test in those binaries — serial only excludes other #[serial] tests, so annotating just the mutator fixes nothing.
  • New smc::DecoyGuard: RAII decoy setter that removes the pairs on drop, so a panicking assertion can't leave the process env poisoned for later tests. (npm and nuget previously never removed their decoys at all.)

Single-test binaries with set_var (setup_matrix_env_guard, e2e_golang_build) have no intra-process race and are untouched.

Validation

All touched binaries run locally: pypi/deno/maven/nuget/safety_cow/vendor_pypi_build green (docker-gated cases skip/fail identically on an untouched checkout — pre-existing local-env, e.g. the known BASELINE GAP cases). The test-release rerun on #133 that motivated this went green with no code change, consistent with the diagnosis.

🤖 Generated with Claude Code

CI flake (seen twice: setup_matrix_pypi
host_guard::poetry_check_recognizes_structural_hook_host, exit 2 with a
clap --help hint): several test binaries set hostile ambient env decoys
PROCESS-WIDE (std::env::set_var) to prove the child-env scrub in their
run helpers works, while sibling tests run on parallel threads. The
scrub snapshots std::env::vars_os() and then spawns; a set_var landing
between a sibling's snapshot and its spawn reaches the child un-scrubbed
and (for SOCKET_STRICT=banana etc.) aborts its arg parse with exit 2.

Mechanism proven RED/GREEN by making the schedule deterministic: a 10ms
delay before the decoy set (simulating the late thread start CI hits)
plus a 25ms window between snapshot and spawn fails 2/3 host_guard
tests on EVERY run without the fix, and 0/5 runs with it (probes not
committed).

Fix, applied to every binary that mutates process env with >1 test
(setup_matrix_{pypi,npm,deno,maven,nuget}, e2e_safety_cow,
e2e_vendor_pypi_build):
- #[serial_test::serial] on every test in the binary — serial only
  excludes other #[serial] tests, so annotating just the mutator would
  fix nothing
- smc::DecoyGuard, an RAII setter that removes the decoys on drop, so a
  panicking assertion can't leave the process env poisoned for the tests
  that run after it (npm and nuget previously never removed their decoys
  at all)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant