Skip to content

Make the idle-reaper, PTY input and late-attach tests deterministic - #456

Merged
Tryanks merged 1 commit into
mainfrom
deflake-timing-tests
Sep 16, 2026
Merged

Tryanks merged 1 commit into
mainfrom
deflake-timing-tests

Conversation

@Tryanks

@Tryanks Tryanks commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Three tests that flaked on CI (each passing on rerun), fixed at the source rather than by lengthening sleeps.

  • resident_idle_reaper_shuts_down_untouched_provider (Windows): the reaper timer runs on smol's executor and only later hops into the host mailbox, so run_until_parked could return before a 1 ms timer fired. The timer body is now AppState::reap_idle_resident (production entry point); the shutdown test waits on the observable condition with the existing run_until seam, and the re-adoption sibling calls the entry point directly, so its negative assertion is no longer vacuous.
  • term::tests::input_reaches_the_child_process (Linux): production fix. GridEmulator::read sampled the grid, dropped the lock, then read exited from the lifecycle lock; if the output bridge fed the last bytes and set_exited in between, a snapshot could report the exit with a stale grid (the CI panic showed an empty grid with exited == true). read now takes the lifecycle lock while still holding the grid lock; set_exited/exited() take only the lifecycle lock, so there is no lock-order cycle. Reproduced by widening the window with a 5 ms sleep on a 2-vCPU loaded container (19/20 failures), 20/20 pass with the fix.
  • late_terminal_attach_reproduces_the_host_grid (Linux): the logged failure ("bash" != "sh" title) predates fix: coalesce terminal burst scrollback across short PTY gaps #448 which pinned titles. The residual hole is the line discipline delivering a sentinel and its \r\n as separate reads, so an attach between them sees a different cursor. Session::wait_for now also requires the host cursor to sit at column 0 below the sentinel row.

Loops (macOS release, and Linux rust:latest --cpus=2 with 4 CPU hogs): each test 30–100 iterations, full term and tcode_runtime binaries repeatedly, 0 failures.

cargo fmt --all --check, cargo clippy --workspace --all-targets --locked -- -D warnings, cargo test --workspace --locked green.

Three tests raced real timers or thread scheduling and failed under CI load.

- resident_idle_reaper_*: the grace timer runs on the background executor,
  so `run_until_parked` could declare the mailbox idle before the 1 ms timer
  landed. The timer callback is now a production entry point,
  `reap_idle_resident`, which the re-adoption test invokes directly with the
  stale idle stamp (its negative assertion was vacuous when the timer had
  not fired), and the shutdown test waits on the reaped state via
  `run_until`.
- input_reaches_the_child_process: `GridEmulator::read` sampled the grid and
  the exit flag under separate locks, so a snapshot could report the exit
  while missing the output fed just before it. The exit state is now read
  while the grid lock is still held, which the test's contract requires.
- late_terminal_attach_reproduces_the_host_grid: the line discipline hands a
  sentinel and its `\r\n` to the PTY master as separate writes, so `wait_for`
  could return with the cursor move still in flight and the attach frame
  would differ from a host grid read moments later. `wait_for` now also
  requires the cursor to sit at column 0 below the sentinel.
@Tryanks
Tryanks merged commit e15a01c into main Sep 16, 2026
6 checks passed
@Tryanks
Tryanks deleted the deflake-timing-tests branch September 16, 2026 21:40
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