mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
a904ef0d5d
The unit-lane corpus replay executed adversarial parser cases on worker threads of the Vitest worker running the test file. A fault in a worker thread ends its whole process, so a case that faulted killed the test runner: `[vitest-pool]: Worker forks emitted error / Worker exited unexpectedly`, with no test, file, or case named. Six of six Coverage deaths before #1994's split were this one file out of ~1100, and the uninstrumented second leg it created then lost the same file six more times in three days. Cases now run in a worker *process*. The two faults a case cannot report about itself are both classified from outside it: a case that never returns is a `hang` (unchanged), and one that ends the process it runs in is a new `crash` failure carrying the exit code or signal and the tail of the worker's stderr — the death certificate the lane used to lose. A sixth self-check target seeds that kind, so a regression in reporting it fails the harness self-check like every other kind.
36 lines
1.8 KiB
TypeScript
36 lines
1.8 KiB
TypeScript
// Case-executing worker process for the parser fuzz lane (#1414).
|
|
//
|
|
// Cases run out of process for two reasons. A synchronous parser that never returns cannot be
|
|
// timed out from inside its own tick, so the budget is enforced from outside — the runner
|
|
// (scripts/fuzz/execute.ts) kills this process when a case stops answering, which is how a hang
|
|
// is attributed to an exact input. And a case that faults the process it runs in cannot be
|
|
// caught at all: only a separate process keeps that fault off the caller, whose thread may be
|
|
// the unit lane's test runner (#2053).
|
|
|
|
import { checkCase, type FuzzFailure } from './invariant.ts';
|
|
import { getFuzzTarget } from './registry.ts';
|
|
|
|
export type FuzzWorkerRequest = { kind: 'case'; input: string };
|
|
|
|
export type FuzzWorkerMessage = { kind: 'ready' } | { kind: 'result'; failure: FuzzFailure | null };
|
|
|
|
const send = process.send?.bind(process);
|
|
if (!send) throw new Error('scripts/fuzz/worker.ts must be run as a forked child process.');
|
|
|
|
const target = getFuzzTarget(process.argv[2] ?? '');
|
|
|
|
// The batch-steps parser warns on deprecated step shapes; a fuzz run would emit thousands of
|
|
// those lines and bury the failure report. Only JavaScript writes go through here: a fatal
|
|
// fault writes to the descriptor itself, which is why the runner keeps this process's stderr
|
|
// piped rather than discarded.
|
|
process.stderr.write = (() => true) as typeof process.stderr.write;
|
|
|
|
process.on('message', (request: FuzzWorkerRequest) => {
|
|
const failure = checkCase(target, request.input);
|
|
send({ kind: 'result', failure } satisfies FuzzWorkerMessage);
|
|
});
|
|
|
|
// Module loading (type stripping, parser imports) can outlast a per-case budget, so the runner
|
|
// only starts a case budget after this handshake.
|
|
send({ kind: 'ready' } satisfies FuzzWorkerMessage);
|