aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/js/node/child_process.js116
-rw-r--r--src/js/out/modules/node/child_process.js62
-rw-r--r--test/js/node/child_process/child_process-node.test.js269
-rw-r--r--test/js/node/child_process/fixtures/child-process-echo-options.js2
-rw-r--r--test/js/node/child_process/fixtures/child-process-spawn-node.js11
-rw-r--r--test/js/node/child_process/fixtures/child-process-stay-alive-forever.js3
-rw-r--r--test/js/node/harness.ts2
7 files changed, 446 insertions, 19 deletions
diff --git a/src/js/node/child_process.js b/src/js/node/child_process.js
index c6b10bbec..edcb51cef 100644
--- a/src/js/node/child_process.js
+++ b/src/js/node/child_process.js
@@ -22,6 +22,8 @@ var ArrayPrototypeMap = Array.prototype.map;
var ArrayPrototypeIncludes = Array.prototype.includes;
var ArrayPrototypeSlice = Array.prototype.slice;
var ArrayPrototypeUnshift = Array.prototype.unshift;
+var ArrayPrototypeLastIndexOf = Array.prototype.lastIndexOf;
+var ArrayPrototypeSplice = Array.prototype.splice;
var ArrayIsArray = Array.isArray;
// var ArrayBuffer = ArrayBuffer;
@@ -194,7 +196,7 @@ export function spawn(file, args, options) {
}
function onAbortListener() {
- abortChildProcess(child, killSignal);
+ abortChildProcess(child, killSignal, options.signal.reason);
}
}
return child;
@@ -680,8 +682,97 @@ export function execSync(command, options) {
return ret.stdout;
}
-export function fork() {
- throw new Error("Not implemented");
+function stdioStringToArray(stdio, channel) {
+ const options = [];
+
+ switch (stdio) {
+ case "ignore":
+ case "overlapped":
+ case "pipe":
+ ArrayPrototypePush.call(options, stdio, stdio, stdio);
+ break;
+ case "inherit":
+ ArrayPrototypePush.call(options, 0, 1, 2);
+ break;
+ default:
+ throw new ERR_INVALID_ARG_VALUE("stdio", stdio);
+ }
+
+ if (channel) ArrayPrototypePush.call(options, channel);
+
+ return options;
+}
+
+/**
+ * Spawns a new Node.js process + fork.
+ * @param {string|URL} modulePath
+ * @param {string[]} [args]
+ * @param {{
+ * cwd?: string;
+ * detached?: boolean;
+ * env?: Record<string, string>;
+ * execPath?: string;
+ * execArgv?: string[];
+ * gid?: number;
+ * serialization?: string;
+ * signal?: AbortSignal;
+ * killSignal?: string | number;
+ * silent?: boolean;
+ * stdio?: Array | string;
+ * uid?: number;
+ * windowsVerbatimArguments?: boolean;
+ * timeout?: number;
+ * }} [options]
+ * @returns {ChildProcess}
+ */
+export function fork(modulePath, args = [], options) {
+ modulePath = getValidatedPath(modulePath, "modulePath");
+
+ // Get options and args arguments.
+ let execArgv;
+
+ if (args == null) {
+ args = [];
+ } else if (typeof args === "object" && !ArrayIsArray(args)) {
+ options = args;
+ args = [];
+ } else {
+ validateArray(args, "args");
+ }
+
+ if (options != null) {
+ validateObject(options, "options");
+ }
+ options = { __proto__: null, ...options, shell: false };
+ options.execPath = options.execPath || process.execPath;
+ validateArgumentNullCheck(options.execPath, "options.execPath");
+
+ // Prepare arguments for fork:
+ execArgv = options.execArgv || process.execArgv;
+ validateArgumentsNullCheck(execArgv, "options.execArgv");
+
+ if (execArgv === process.execArgv && process._eval != null) {
+ const index = ArrayPrototypeLastIndexOf.call(execArgv, process._eval);
+ if (index > 0) {
+ // Remove the -e switch to avoid fork bombing ourselves.
+ execArgv = ArrayPrototypeSlice.call(execArgv);
+ ArrayPrototypeSplice.call(execArgv, index - 1, 2);
+ }
+ }
+
+ args = [...execArgv, modulePath, ...args];
+
+ if (typeof options.stdio === "string") {
+ options.stdio = stdioStringToArray(options.stdio, "ipc");
+ } else if (!ArrayIsArray(options.stdio)) {
+ // Use a separate fd=3 for the IPC channel. Inherit stdin, stdout,
+ // and stderr from the parent if silent isn't set.
+ options.stdio = stdioStringToArray(options.silent ? "pipe" : "inherit", "ipc");
+ } else if (!ArrayPrototypeIncludes.call(options.stdio, "ipc")) {
+ throw new ERR_CHILD_PROCESS_IPC_REQUIRED("options.stdio");
+ }
+
+ return spawn(options.execPath, args, options);
}
//------------------------------------------------------------------------------
@@ -909,8 +1000,11 @@ export class ChildProcess extends EventEmitter {
#handleOnExit(exitCode, signalCode, err) {
if (this.#exited) return;
- this.exitCode = this.#handle.exitCode;
- this.signalCode = exitCode > 0 ? signalCode : null;
+ if (signalCode) {
+ this.signalCode = signalCode;
+ } else {
+ this.exitCode = exitCode;
+ }
if (this.#stdin) {
this.#stdin.destroy();
@@ -1286,11 +1380,11 @@ function onSpawnNT(self) {
self.emit("spawn");
}
-function abortChildProcess(child, killSignal) {
+function abortChildProcess(child, killSignal, reason) {
if (!child) return;
try {
if (child.kill(killSignal)) {
- child.emit("error", new AbortError());
+ child.emit("error", new AbortError(undefined, { cause: reason }));
}
} catch (err) {
child.emit("error", err);
@@ -1696,7 +1790,7 @@ function ERR_UNKNOWN_SIGNAL(name) {
}
function ERR_INVALID_ARG_TYPE(name, type, value) {
- const err = new TypeError(`The "${name}" argument must be of type ${type}. Received ${value}`);
+ const err = new TypeError(`The "${name}" argument must be of type ${type}. Received ${value?.toString()}`);
err.code = "ERR_INVALID_ARG_TYPE";
return err;
}
@@ -1709,6 +1803,12 @@ function ERR_INVALID_ARG_VALUE(name, value, reason) {
return new Error(`The value "${value}" is invalid for argument '${name}'. Reason: ${reason}`);
}
+function ERR_CHILD_PROCESS_IPC_REQUIRED(name) {
+ const err = new TypeError(`Forked processes must have an IPC channel, missing value 'ipc' in ${name}`);
+ err.code = "ERR_CHILD_PROCESS_IPC_REQUIRED";
+ return err;
+}
+
class SystemError extends Error {
path;
syscall;
diff --git a/src/js/out/modules/node/child_process.js b/src/js/out/modules/node/child_process.js
index 042fb7753..09d52e822 100644
--- a/src/js/out/modules/node/child_process.js
+++ b/src/js/out/modules/node/child_process.js
@@ -23,7 +23,7 @@ function spawn(file, args, options) {
}
if (options.signal) {
let onAbortListener = function() {
- abortChildProcess(child, killSignal2);
+ abortChildProcess(child, killSignal2, options.signal.reason);
};
const signal = options.signal;
if (signal.aborted)
@@ -224,8 +224,47 @@ function execSync(command, options) {
throw err;
return ret.stdout;
}
-function fork() {
- throw new Error("Not implemented");
+var stdioStringToArray = function(stdio, channel) {
+ const options = [];
+ switch (stdio) {
+ case "ignore":
+ case "overlapped":
+ case "pipe":
+ ArrayPrototypePush.call(options, stdio, stdio, stdio);
+ break;
+ case "inherit":
+ ArrayPrototypePush.call(options, 0, 1, 2);
+ break;
+ default:
+ throw new ERR_INVALID_ARG_VALUE("stdio", stdio);
+ }
+ if (channel)
+ ArrayPrototypePush.call(options, channel);
+ return options;
+};
+function fork(modulePath, args = [], options) {
+ modulePath = getValidatedPath(modulePath, "modulePath");
+ let execArgv;
+ if (args == null)
+ args = [];
+ else if (typeof args === "object" && !ArrayIsArray(args))
+ options = args, args = [];
+ else
+ validateArray(args, "args");
+ if (options != null)
+ validateObject(options, "options");
+ if (options = { __proto__: null, ...options, shell: !1 }, options.execPath = options.execPath || process.execPath, validateArgumentNullCheck(options.execPath, "options.execPath"), execArgv = options.execArgv || process.execArgv, validateArgumentsNullCheck(execArgv, "options.execArgv"), execArgv === process.execArgv && process._eval != null) {
+ const index = ArrayPrototypeLastIndexOf.call(execArgv, process._eval);
+ if (index > 0)
+ execArgv = ArrayPrototypeSlice.call(execArgv), ArrayPrototypeSplice.call(execArgv, index - 1, 2);
+ }
+ if (args = [...execArgv, modulePath, ...args], typeof options.stdio === "string")
+ options.stdio = stdioStringToArray(options.stdio, "ipc");
+ else if (!ArrayIsArray(options.stdio))
+ options.stdio = stdioStringToArray(options.silent ? "pipe" : "inherit", "ipc");
+ else if (!ArrayPrototypeIncludes.call(options.stdio, "ipc"))
+ throw new ERR_CHILD_PROCESS_IPC_REQUIRED("options.stdio");
+ return spawn(options.execPath, args, options);
}
var convertToValidSignal = function(signal) {
if (typeof signal === "number" && getSignalsToNamesMapping()[signal])
@@ -383,12 +422,12 @@ var convertToValidSignal = function(signal) {
}
}, onSpawnNT = function(self) {
self.emit("spawn");
-}, abortChildProcess = function(child, killSignal2) {
+}, abortChildProcess = function(child, killSignal2, reason) {
if (!child)
return;
try {
if (child.kill(killSignal2))
- child.emit("error", new AbortError);
+ child.emit("error", new AbortError(void 0, { cause: reason }));
} catch (err) {
child.emit("error", err);
}
@@ -447,13 +486,16 @@ var validateFunction = function(value, name) {
const err = new TypeError(`Unknown signal: ${name}`);
return err.code = "ERR_UNKNOWN_SIGNAL", err;
}, ERR_INVALID_ARG_TYPE = function(name, type, value) {
- const err = new TypeError(`The "${name}" argument must be of type ${type}. Received ${value}`);
+ const err = new TypeError(`The "${name}" argument must be of type ${type}. Received ${value?.toString()}`);
return err.code = "ERR_INVALID_ARG_TYPE", err;
}, ERR_INVALID_OPT_VALUE = function(name, value) {
return new TypeError(`The value "${value}" is invalid for option "${name}"`);
}, ERR_INVALID_ARG_VALUE = function(name, value, reason) {
return new Error(`The value "${value}" is invalid for argument '${name}'. Reason: ${reason}`);
-}, signals = constants.signals, { ArrayBuffer, Uint8Array, String, Object, Buffer, Promise: Promise2 } = globalThis[Symbol.for("Bun.lazy")]("primordials"), ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty, ObjectCreate = Object.create, ObjectAssign = Object.assign, ObjectDefineProperty = Object.defineProperty, BufferConcat = Buffer.concat, BufferIsEncoding = Buffer.isEncoding, kEmptyObject = ObjectCreate(null), ArrayPrototypePush = Array.prototype.push, ArrayPrototypeJoin = Array.prototype.join, ArrayPrototypeMap = Array.prototype.map, ArrayPrototypeIncludes = Array.prototype.includes, ArrayPrototypeSlice = Array.prototype.slice, ArrayPrototypeUnshift = Array.prototype.unshift, ArrayIsArray = Array.isArray, ArrayBufferIsView = ArrayBuffer.isView, NumberIsInteger = Number.isInteger;
+}, ERR_CHILD_PROCESS_IPC_REQUIRED = function(name) {
+ const err = new TypeError(`Forked processes must have an IPC channel, missing value 'ipc' in ${name}`);
+ return err.code = "ERR_CHILD_PROCESS_IPC_REQUIRED", err;
+}, signals = constants.signals, { ArrayBuffer, Uint8Array, String, Object, Buffer, Promise: Promise2 } = globalThis[Symbol.for("Bun.lazy")]("primordials"), ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty, ObjectCreate = Object.create, ObjectAssign = Object.assign, ObjectDefineProperty = Object.defineProperty, BufferConcat = Buffer.concat, BufferIsEncoding = Buffer.isEncoding, kEmptyObject = ObjectCreate(null), ArrayPrototypePush = Array.prototype.push, ArrayPrototypeJoin = Array.prototype.join, ArrayPrototypeMap = Array.prototype.map, ArrayPrototypeIncludes = Array.prototype.includes, ArrayPrototypeSlice = Array.prototype.slice, ArrayPrototypeUnshift = Array.prototype.unshift, ArrayPrototypeLastIndexOf = Array.prototype.lastIndexOf, ArrayPrototypeSplice = Array.prototype.splice, ArrayIsArray = Array.isArray, ArrayBufferIsView = ArrayBuffer.isView, NumberIsInteger = Number.isInteger;
var StringPrototypeToUpperCase = String.prototype.toUpperCase, StringPrototypeIncludes = String.prototype.includes, StringPrototypeSlice = String.prototype.slice, Uint8ArrayPrototypeIncludes = Uint8Array.prototype.includes, MAX_BUFFER = 1048576, __DEBUG__ = process.env.DEBUG || !1, __TRACK_STDIO__ = process.env.DEBUG_STDIO, debug = __DEBUG__ ? console.log : () => {
};
if (__TRACK_STDIO__)
@@ -500,7 +542,11 @@ class ChildProcess extends EventEmitter {
#handleOnExit(exitCode, signalCode, err) {
if (this.#exited)
return;
- if (this.exitCode = this.#handle.exitCode, this.signalCode = exitCode > 0 ? signalCode : null, this.#stdin)
+ if (signalCode)
+ this.signalCode = signalCode;
+ else
+ this.exitCode = exitCode;
+ if (this.#stdin)
this.#stdin.destroy();
if (this.#handle)
this.#handle = null;
diff --git a/test/js/node/child_process/child_process-node.test.js b/test/js/node/child_process/child_process-node.test.js
index 579ddbd5e..d8b48747e 100644
--- a/test/js/node/child_process/child_process-node.test.js
+++ b/test/js/node/child_process/child_process-node.test.js
@@ -1,6 +1,7 @@
-import { ChildProcess, spawn, exec } from "node:child_process";
+import { ChildProcess, spawn, exec, fork } from "node:child_process";
import { createTest } from "node-harness";
import { tmpdir } from "node:os";
+import path from "node:path";
import { bunExe } from "harness";
const { beforeAll, describe, expect, it, throws, assert, createCallCheckCtx, createDoneDotAll } = createTest(
import.meta.path,
@@ -12,6 +13,14 @@ const platformTmpDir = require("fs").realpathSync(tmpdir());
const TYPE_ERR_NAME = "TypeError";
+const fixturesDir = path.join(__dirname, "fixtures");
+
+const fixtures = {
+ path(...args) {
+ return path.join(fixturesDir, ...args);
+ },
+};
+
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@@ -483,3 +492,261 @@ describe("child_process double pipe", () => {
);
});
});
+
+describe("fork", () => {
+ const expectedEnv = { foo: "bar" };
+ describe("abort-signal", () => {
+ it("Test aborting a forked child_process after calling fork", done => {
+ const { mustCall } = createCallCheckCtx(done);
+ const ac = new AbortController();
+ const { signal } = ac;
+ const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), {
+ signal,
+ });
+ cp.on(
+ "exit",
+ mustCall((code, killSignal) => {
+ strictEqual(code, null);
+ strictEqual(killSignal, "SIGTERM");
+ }),
+ );
+ cp.on(
+ "error",
+ mustCall(err => {
+ strictEqual(err.name, "AbortError");
+ }),
+ );
+ process.nextTick(() => ac.abort());
+ });
+ it("Test aborting with custom error", done => {
+ const { mustCall } = createCallCheckCtx(done);
+ const ac = new AbortController();
+ const { signal } = ac;
+ const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), {
+ signal,
+ });
+ cp.on(
+ "exit",
+ mustCall((code, killSignal) => {
+ strictEqual(code, null);
+ strictEqual(killSignal, "SIGTERM");
+ }),
+ );
+ cp.on(
+ "error",
+ mustCall(err => {
+ strictEqual(err.name, "AbortError");
+ strictEqual(err.cause.name, "Error");
+ strictEqual(err.cause.message, "boom");
+ }),
+ );
+ process.nextTick(() => ac.abort(new Error("boom")));
+ });
+ it("Test passing an already aborted signal to a forked child_process", done => {
+ const { mustCall } = createCallCheckCtx(done);
+ const signal = AbortSignal.abort();
+ const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), {
+ signal,
+ });
+ cp.on(
+ "exit",
+ mustCall((code, killSignal) => {
+ strictEqual(code, null);
+ strictEqual(killSignal, "SIGTERM");
+ }),
+ );
+ cp.on(
+ "error",
+ mustCall(err => {
+ strictEqual(err.name, "AbortError");
+ }),
+ );
+ });
+ it("Test passing an aborted signal with custom error to a forked child_process", done => {
+ const { mustCall } = createCallCheckCtx(done);
+ const signal = AbortSignal.abort(new Error("boom"));
+ const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), {
+ signal,
+ });
+ cp.on(
+ "exit",
+ mustCall((code, killSignal) => {
+ strictEqual(code, null);
+ strictEqual(killSignal, "SIGTERM");
+ }),
+ );
+ cp.on(
+ "error",
+ mustCall(err => {
+ strictEqual(err.name, "AbortError");
+ strictEqual(err.cause.name, "Error");
+ strictEqual(err.cause.message, "boom");
+ }),
+ );
+ });
+ it("Test passing a different kill signal", done => {
+ const { mustCall } = createCallCheckCtx(done);
+ const signal = AbortSignal.abort();
+ const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), {
+ signal,
+ killSignal: "SIGKILL",
+ });
+ cp.on(
+ "exit",
+ mustCall((code, killSignal) => {
+ strictEqual(code, null);
+ strictEqual(killSignal, "SIGKILL");
+ }),
+ );
+ cp.on(
+ "error",
+ mustCall(err => {
+ strictEqual(err.name, "AbortError");
+ }),
+ );
+ });
+ it("Test aborting a cp before close but after exit", done => {
+ const { mustCall, mustNotCall } = createCallCheckCtx(done);
+ const ac = new AbortController();
+ const { signal } = ac;
+ const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), {
+ signal,
+ });
+ cp.on(
+ "exit",
+ mustCall(() => {
+ ac.abort();
+ }),
+ );
+ cp.on("error", mustNotCall());
+
+ setTimeout(() => cp.kill(), 1);
+ });
+ });
+ describe("args", () => {
+ it("Ensure that first argument `modulePath` must be provided and be of type string", () => {
+ const invalidModulePath = [0, true, undefined, null, [], {}, () => {}, Symbol("t")];
+ invalidModulePath.forEach(modulePath => {
+ expect(() => fork(modulePath)).toThrow({
+ code: "ERR_INVALID_ARG_TYPE",
+ name: "TypeError",
+ message: `The "modulePath" argument must be of type string,Buffer,URL. Received ${modulePath?.toString()}`,
+ });
+ });
+ });
+ it("Ensure that the second argument of `fork` and `fork` should parse options correctly if args is undefined or null", done => {
+ const invalidSecondArgs = [0, true, () => {}, Symbol("t")];
+ invalidSecondArgs.forEach(arg => {
+ expect(() => fork(fixtures.path("child-process-echo-options.js"), arg)).toThrow({
+ code: "ERR_INVALID_ARG_TYPE",
+ name: "TypeError",
+ message: `The \"args\" argument must be of type Array. Received ${arg?.toString()}`,
+ });
+ });
+
+ const argsLists = [undefined, null, []];
+
+ const { mustCall } = createCallCheckCtx(done);
+
+ argsLists.forEach(args => {
+ const cp = fork(fixtures.path("child-process-echo-options.js"), args, {
+ env: { ...process.env, ...expectedEnv },
+ });
+
+ // TODO - bun has no `send` method in the process
+ // cp.on(
+ // 'message',
+ // common.mustCall(({ env }) => {
+ // assert.strictEqual(env.foo, expectedEnv.foo);
+ // })
+ // );
+
+ cp.on(
+ "exit",
+ mustCall(code => {
+ assert.strictEqual(code, 0);
+ }),
+ );
+ });
+ });
+ it("Ensure that the third argument should be type of object if provided", () => {
+ const invalidThirdArgs = [0, true, () => {}, Symbol("t")];
+ invalidThirdArgs.forEach(arg => {
+ expect(() => {
+ fork(fixtures.path("child-process-echo-options.js"), [], arg);
+ }).toThrow({
+ code: "ERR_INVALID_ARG_TYPE",
+ name: "TypeError",
+ message: `The \"options\" argument must be of type object. Received ${arg?.toString()}`,
+ });
+ });
+ });
+ });
+ describe.todo("close", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-close.js
+ });
+ describe.todo("detached", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-detached.js
+ });
+ describe.todo("dgram", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-dgram.js
+ });
+ describe.todo("net", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-net.js
+ });
+ describe.todo("net-server", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-net-server.js
+ });
+ describe.todo("net-socket", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-net-socket.js
+ });
+ describe.todo("no-shell", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-no-shell.js
+ });
+ describe.todo("ref", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-ref.js
+ });
+ describe.todo("stdio", () => {
+ // https://github.com/nodejs/node/blob/v20.5.0/test/parallel/test-child-process-fork-stdio.js
+ });
+ describe("fork", () => {
+ it("message", done => {
+ // TODO - bun has no `send` method in the process
+ done();
+ // const { mustCall } = createCallCheckCtx(done);
+ // const args = ['foo', 'bar'];
+ // const n = fork(fixtures.path('child-process-spawn-node.js'), args);
+
+ // assert.strictEqual(n.channel, n._channel);
+ // assert.deepStrictEqual(args, ['foo', 'bar']);
+
+ // n.on('message', (m) => {
+ // debug('PARENT got message:', m);
+ // assert.ok(m.foo);
+ // });
+
+ // expect(() => n.send(undefined)).toThrow({
+ // name: 'TypeError',
+ // message: 'The "message" argument must be specified',
+ // code: 'ERR_MISSING_ARGS'
+ // });
+ // expect(() => n.send()).toThrow({
+ // name: 'TypeError',
+ // message: 'The "message" argument must be specified',
+ // code: 'ERR_MISSING_ARGS'
+ // });
+
+ // expect(() => n.send(Symbol())).toThrow({
+ // name: 'TypeError',
+ // message: 'The "message" argument must be one of type string,' +
+ // ' object, number, or boolean. Received type symbol (Symbol())',
+ // code: 'ERR_INVALID_ARG_TYPE'
+ // });
+ // n.send({ hello: 'world' });
+
+ // n.on('exit', mustCall((c) => {
+ // assert.strictEqual(c, 0);
+ // }));
+ });
+ });
+});
diff --git a/test/js/node/child_process/fixtures/child-process-echo-options.js b/test/js/node/child_process/fixtures/child-process-echo-options.js
new file mode 100644
index 000000000..7d6298bd0
--- /dev/null
+++ b/test/js/node/child_process/fixtures/child-process-echo-options.js
@@ -0,0 +1,2 @@
+// TODO - bun has no `send` method in the process
+process?.send({ env: process.env });
diff --git a/test/js/node/child_process/fixtures/child-process-spawn-node.js b/test/js/node/child_process/fixtures/child-process-spawn-node.js
new file mode 100644
index 000000000..a462c106e
--- /dev/null
+++ b/test/js/node/child_process/fixtures/child-process-spawn-node.js
@@ -0,0 +1,11 @@
+const assert = require("assert");
+const debug = require("util").debuglog("test");
+
+function onmessage(m) {
+ debug("CHILD got message:", m);
+ assert.ok(m.hello);
+ process.removeListener("message", onmessage);
+}
+
+process.on("message", onmessage);
+process?.send({ foo: "bar" });
diff --git a/test/js/node/child_process/fixtures/child-process-stay-alive-forever.js b/test/js/node/child_process/fixtures/child-process-stay-alive-forever.js
new file mode 100644
index 000000000..d912ca3a3
--- /dev/null
+++ b/test/js/node/child_process/fixtures/child-process-stay-alive-forever.js
@@ -0,0 +1,3 @@
+setInterval(() => {
+ // Starting an interval to stay alive.
+}, 1000);
diff --git a/test/js/node/harness.ts b/test/js/node/harness.ts
index 9cea1b781..bd34f541a 100644
--- a/test/js/node/harness.ts
+++ b/test/js/node/harness.ts
@@ -107,8 +107,6 @@ export function createTest(path: string) {
}
function mustNotCall(reason: string = "function should not have been called", optionalCb?: (err?: any) => void) {
- const localDone = createDone();
- timers.push(setTimeout(() => localDone(), 200));
return () => {
closeTimers();
if (optionalCb) optionalCb.apply(undefined, reason ? [reason] : []);