Skip to content

test(core): poll the installer PTY instead of racing it - #53

Merged
StvLi merged 1 commit into
mainfrom
fix/pty-status-race
Sep 29, 2026
Merged

StvLi merged 1 commit into
mainfrom
fix/pty-status-race

Conversation

@StvLi

@StvLi StvLi commented Sep 29, 2026

Copy link
Copy Markdown
Owner

A docs-only PR (#52) went red on check (22), and the same job passed on an immediate re-run. That is the PTY test, and this fixes it.

Evidence

FAIL tests/tools.spec.ts > ros2_install interactive flow (mock installer, no network)
     > start -> send -> status -> stop drives the installer menus via PTY
AssertionError: expected '' to contain '众多工具'
Run check (22)
PR #52, first run fail
PR #52, re-run, no code change pass
PRs #48 / #49 / #50 / #51, both Node versions pass

Cause

Not the environment — a race in the test. ros2_install drives the installer inside a pty daemon whose output arrives asynchronously, but the test read status exactly once, immediately after start, and required the menu to be present already:

await t.execute({ action: 'start', ... })
const s1 = await t.execute({ action: 'status', session })   // ← no wait at all
expect(s1.data.output).toContain('众多工具')                  // ← relies on the daemon having won

The next two steps were the same hazard written as a guessed duration — setTimeout(800) and setTimeout(2500). Those lower the odds without removing the race, and pay that cost on every single run.

Fix

statusUntil(t, session, predicate, timeoutMs) — a bounded 100 ms poll that returns the moment the awaited output appears. Each step now waits for the specific thing it asserts instead of for a duration, and the final step's predicate also requires state to have reached exited rather than assuming output and state flip together. Timeout 15 s → 30 s so a genuine failure still reports as which assertion never arrived, not a generic timeout.

Verification

pnpm run typecheck passes. The test is it.skipIf(!ptyUsable), and this host cannot allocate a pty, so the PTY path is exercised by CI only — the local-skip / CI-truth split already recorded in §19.8 of the maintenance log. Both check (22) and check (24) are the real verification here.

Worth noting as a category, since this repository has now hit it twice: a flaky gate is a maintenance liability in its own right. The maintain log's own guidance is "push → open a PR → wait for CI green before merging", and that only means something while green is informative.

`check (22)` failed on a docs-only PR with

    FAIL tests/tools.spec.ts > ros2_install interactive flow (mock installer, no network)
         > start -> send -> status -> stop drives the installer menus via PTY
    AssertionError: expected '' to contain '众多工具'

and the identical job passed on an immediate re-run — as it had on every
neighbouring PR and on both Node versions. A test that reds a docs-only change
on a coin flip is worse than no test: it teaches everyone to re-run CI until it
turns green, which is exactly the signal this repository's merge discipline
depends on.

The cause is a race, not an environment quirk. `ros2_install` drives the
installer inside a pty daemon whose output arrives asynchronously, but the test
read `status` exactly once, immediately after `start`, and required the menu to
be there already. The two following steps guessed fixed durations
(`setTimeout(800)` and `setTimeout(2500)`) — the same hazard written as a delay,
which lowers the odds without removing the race and pays that cost on every run.

Replace all three with `statusUntil(...)`: a bounded 100 ms poll on a predicate,
returning the moment the awaited output actually appears. The final step's
predicate requires `state` to have reached `exited` too, rather than assuming
output and state flip together. The test timeout grows 15 s -> 30 s so that a
real failure still surfaces as the specific assertion that never arrived instead
of a generic timeout.

This path is `it.skipIf(!ptyUsable)`, and this host cannot allocate a pty, so the
change is verified by CI (`check (22)` / `check (24)`) and not locally — the same
local-skip/CI-truth split recorded in §19.8 of the maintenance log.
@StvLi
StvLi merged commit 7bdfc85 into main Sep 29, 2026
5 checks passed
StvLi added a commit that referenced this pull request Sep 29, 2026
docs: record the flake fix (#53) in the round-16 log
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