Skip to content

Commit 518b3e2

Browse files
committed
refactor: make spawnTest result runtime-agnostic
Replace the POSIX-specific signal field with an aborted boolean computed by the Node implementor, moving the abort exit-code knowledge out of portable tests and into child_process.js.
1 parent 5d3b3f2 commit 518b3e2

4 files changed

Lines changed: 20 additions & 8 deletions

File tree

implementors/node/child_process.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export interface SpawnTestOptions {
44

55
export interface SpawnTestResult {
66
status: number | null;
7-
signal: NodeJS.Signals | null;
7+
aborted: boolean;
88
stdout: string;
99
stderr: string;
1010
}

implementors/node/child_process.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,24 @@ const HARNESS_MODULE_PATHS = [
1212
'child_process.js',
1313
].map((file) => path.join(import.meta.dirname, file));
1414

15+
// Exit codes that signify the runtime aborted (rather than exiting cleanly with
16+
// a non-zero status). On POSIX an abort surfaces as a fatal signal; on Windows
17+
// as one of a small set of exit codes. Mirrors Node.js's
18+
// `common.nodeProcessAborted`. The POSIX 128+N codes and the Windows NTSTATUS
19+
// codes share one list because the ranges don't overlap. Keeping this here, in
20+
// the Node implementor, lets portable tests ask "did it abort?" via the
21+
// `aborted` flag without encoding any runtime-specific process semantics.
22+
const ABORT_EXIT_CODES = [132, 133, 134, 139, 0xc0000409, 0xc000001d];
23+
1524
/**
1625
* Runs a test file in a fresh Node.js subprocess with the CTS harness globals
17-
* pre-loaded, and returns its exit status, signal, and captured output.
26+
* pre-loaded, and returns its exit status, whether it aborted, and output.
1827
*
1928
* @param {string} filePath - Path to the JS/MJS file to execute. Resolved
2029
* against `options.cwd` if relative.
2130
* @param {{ cwd?: string }} [options]
2231
* - `cwd`: working directory for the child; defaults to `process.cwd()`.
23-
* @returns {{ status: number | null, signal: NodeJS.Signals | null, stdout: string, stderr: string }}
32+
* @returns {{ status: number | null, aborted: boolean, stdout: string, stderr: string }}
2433
*/
2534
export const spawnTest = (filePath, options = {}) => {
2635
// --expose-gc is mandatory: gc.js (loaded below) throws at import without it.
@@ -37,7 +46,7 @@ export const spawnTest = (filePath, options = {}) => {
3746
if (result.error) throw result.error;
3847
return {
3948
status: result.status,
40-
signal: result.signal,
49+
aborted: result.signal !== null || ABORT_EXIT_CODES.includes(result.status),
4150
stderr: result.stderr?.toString() ?? '',
4251
stdout: result.stdout?.toString() ?? '',
4352
};

implementors/node/tests.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ export function listDirectoryEntries(dir: string) {
3232
}
3333

3434
export function runFileInSubprocess(cwd: string, filePath: string): void {
35-
const { status, signal, stdout, stderr } = spawnTest(filePath, { cwd });
35+
const { status, aborted, stdout, stderr } = spawnTest(filePath, { cwd });
3636

3737
if (stdout) process.stdout.write(stdout);
3838

3939
if (status === 0) return;
4040

4141
const reason =
42-
status !== null ? `exit code ${status}` : `signal ${signal ?? 'unknown'}`;
42+
status !== null ? `exit code ${status}` : aborted ? 'aborted' : 'unknown';
4343
const trimmedStderr = stderr.trim();
4444
const stderrSuffix = trimmedStderr ?
4545
`\n--- stderr ---\n${trimmedStderr}\n--- end stderr ---` :

tests/harness/spawn-test.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ if (typeof spawnTest !== 'function') {
88
{
99
const result = spawnTest('spawn-test-ok-child.mjs');
1010
assert.strictEqual(result.status, 0, `ok child exited with status ${result.status}; stderr:\n${result.stderr}`);
11-
assert.strictEqual(result.signal, null);
11+
assert.strictEqual(result.aborted, false);
1212
assert.strictEqual(result.stderr, '');
1313
}
1414

1515
// Failing child: non-zero status and stderr contains the thrown marker.
1616
{
1717
const result = spawnTest('spawn-test-fail-child.mjs');
1818
assert.notStrictEqual(result.status, 0, 'fail child should exit non-zero');
19+
// A thrown error is a clean non-zero exit, not an abnormal termination.
20+
assert.strictEqual(result.aborted, false);
1921
if (!result.stderr.includes('spawn-test-fail-marker')) {
2022
throw new Error(`Expected stderr to include the failure marker, got:\n${result.stderr}`);
2123
}
@@ -24,11 +26,12 @@ if (typeof spawnTest !== 'function') {
2426
// Result shape: all four fields are present.
2527
{
2628
const result = spawnTest('spawn-test-ok-child.mjs');
27-
for (const key of ['status', 'signal', 'stdout', 'stderr']) {
29+
for (const key of ['status', 'aborted', 'stdout', 'stderr']) {
2830
if (!(key in result)) {
2931
throw new Error(`Expected spawnTest result to have "${key}" field`);
3032
}
3133
}
34+
assert.strictEqual(typeof result.aborted, 'boolean');
3235
assert.strictEqual(typeof result.stdout, 'string');
3336
assert.strictEqual(typeof result.stderr, 'string');
3437
}

0 commit comments

Comments
 (0)