Skip to content

test: cover crash-path reaping and the relocation guard - #17

Merged
stag175 merged 1 commit into
mainfrom
test/verification-debt
Jul 23, 2026
Merged

test: cover crash-path reaping and the relocation guard#17
stag175 merged 1 commit into
mainfrom
test/verification-debt

Conversation

@stag175

@stag175 stag175 commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Closes the two verification gaps the Electron migration audit identified. The migration deleted a 136-test Rust supervisor suite — including the kernel-level orphan-prevention test and 7 relocation cases — and the replacement coverage missed both behaviours.

Crash path

The existing reap test drives watchdog.close(), which is the graceful path: the child reaps because its stdin closed. That's a normal quit, not the reason the crash guard exists. Three new tests cover the abrupt case:

Test What it pins
liveness poll reaps after parent SIGKILL the poll specifically — the watchdog's stdin is held open so the close-driven path can't be what reaps
SIGKILLed main doesn't orphan its grandchild the end-to-end guarantee, using the sidecar manager's own spawn options
watchdog refuses a pid parented elsewhere the PID-reuse / ownership guard

Two findings from writing them

The fixture child must be spawned detached. With the sidecar manager's own non-detached options on win32, libuv's job object reaps it first — so the test passed with the liveness poll disabled. It was passing for the wrong reason. Detaching puts the child outside that job so only the watchdog can kill it.

The end-to-end test still passes with the poll disabled, and now says so. On Windows the guarantee users actually depend on comes from libuv's KILL_ON_JOB_CLOSE job — an undocumented side effect of detached: false. That's worth pinning explicitly rather than letting the test read as watchdog coverage it isn't.

Relocation guard

The existing test only exercised process.platform, and the electron-runtime CI job runs windows-latest only — so the macOS and Linux branches were entirely unverified. A wrong path there silently strands every Mac and Linux user's question bank. Added:

  • resolution for win32 / darwin / linux including the XDG_DATA_HOME fallback and macOS keeping its own layout
  • an unresolvable environment (no APPDATA/HOME/XDG_DATA_HOME) must not throw — this runs at boot before any window exists, so throwing is an unrecoverable startup crash
  • legacy and current directories must never collide; the same-path branch is unreachable today, and this fails first if a leaf constant is renamed into a collision

Verification

Every test was mutation-verified, not assumed:

  • disabling the liveness poll (if (false && !parentIsAlive())) → crash test fails
  • pointing the darwin branch at the Linux layout → platform test fails

Suite: 60 Electron tests passing (was 55), npx eslint electron/ clean, source files confirmed unmodified after mutation runs.

🤖 Generated with Claude Code

The Electron migration deleted a 136-test Rust supervisor suite, including the
kernel-level orphan-prevention test and 7 relocation cases, and replaced them
with coverage that missed both behaviours entirely. These were the two gaps the
migration audit called out as unverified.

Crash path (electron/tests/crash-recovery.test.mjs, + two fixtures):
The existing reap test drives watchdog.close(), which is the GRACEFUL path — the
child reaps from its stdin closing. That is a normal quit, not the reason the
crash guard exists. Three tests now cover the abrupt case:
- the liveness poll alone reaps a tracked child after its guarded parent is
  SIGKILLed, with the watchdog's stdin deliberately held open so the reap cannot
  come from the close-driven path;
- a SIGKILLed main process does not orphan its sidecar grandchild, end to end,
  using the sidecar manager's own spawn options;
- the watchdog refuses to kill a tracked pid the OS reports as parented
  elsewhere, which is the PID-reuse/ownership guard.

Two findings from building these, both recorded in the tests:
- The fixture child must be spawned DETACHED. With the sidecar manager's own
  non-detached options on win32, libuv's job object reaps it first, so the test
  passed with the liveness poll disabled — it was passing for the wrong reason.
  Detaching puts the child outside that job so only the watchdog can kill it.
- The end-to-end test still passes with the poll disabled, and says so: on
  Windows the guarantee users actually depend on comes from libuv's job object,
  an undocumented side effect of `detached: false` that this test now pins.

Relocation guard (electron/tests/runtime.test.mjs):
The existing test only exercised process.platform, and the electron CI job runs
windows-latest only, so the macOS and Linux branches were entirely unverified —
a wrong path there strands every Mac and Linux user's question bank silently.
Added resolution coverage for win32/darwin/linux including the XDG fallback,
plus the two Rust cases with no JS equivalent: an unresolvable environment must
not throw during boot, and the legacy and current directories must never
collide (the same-path branch is unreachable today; the test fails first if a
leaf constant is renamed into a collision).

Every test was mutation-verified rather than assumed: disabling the liveness
poll fails the crash test, and pointing the darwin branch at the Linux layout
fails the platform test.
@stag175
stag175 merged commit 4b4f055 into main Jul 23, 2026
0 of 9 checks passed
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