diff options
author | 2023-07-29 02:00:43 +0300 | |
---|---|---|
committer | 2023-07-28 16:00:43 -0700 | |
commit | 9078b1286d49d69da435256e80ab0b2e21230b18 (patch) | |
tree | 9d977611014734d6fd7d70fc2f4f1b3b848d5264 | |
parent | 242d8655d854c1b818c38d9b021a31d673638e1e (diff) | |
download | bun-9078b1286d49d69da435256e80ab0b2e21230b18.tar.gz bun-9078b1286d49d69da435256e80ab0b2e21230b18.tar.zst bun-9078b1286d49d69da435256e80ab0b2e21230b18.zip |
add fork to child_process (#3851)
* add fork to child_process
* fix export
* add test to child_process method `fork`
* fmt fork
* remove only from test
-rw-r--r-- | src/js/node/child_process.js | 116 | ||||
-rw-r--r-- | src/js/out/modules/node/child_process.js | 62 | ||||
-rw-r--r-- | test/js/node/child_process/child_process-node.test.js | 269 | ||||
-rw-r--r-- | test/js/node/child_process/fixtures/child-process-echo-options.js | 2 | ||||
-rw-r--r-- | test/js/node/child_process/fixtures/child-process-spawn-node.js | 11 | ||||
-rw-r--r-- | test/js/node/child_process/fixtures/child-process-stay-alive-forever.js | 3 | ||||
-rw-r--r-- | test/js/node/harness.ts | 2 |
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] : []); |