aboutsummaryrefslogtreecommitdiff
path: root/test/bun.js/child_process-node.test.js
diff options
context:
space:
mode:
authorGravatar Ashcon Partovi <ashcon@partovi.net> 2023-03-07 12:22:34 -0800
committerGravatar GitHub <noreply@github.com> 2023-03-07 12:22:34 -0800
commitf7e4eb83694aa007a492ef66c28ffbe6a2dae791 (patch)
tree7af25aa5c42a2e1b2b47ba1df35f8caa9054cbeb /test/bun.js/child_process-node.test.js
parent36275a44ce7a33587bd26aad120042ab95470ff3 (diff)
downloadbun-f7e4eb83694aa007a492ef66c28ffbe6a2dae791.tar.gz
bun-f7e4eb83694aa007a492ef66c28ffbe6a2dae791.tar.zst
bun-f7e4eb83694aa007a492ef66c28ffbe6a2dae791.zip
Reorganize tests (#2332)
Diffstat (limited to 'test/bun.js/child_process-node.test.js')
-rw-r--r--test/bun.js/child_process-node.test.js482
1 files changed, 0 insertions, 482 deletions
diff --git a/test/bun.js/child_process-node.test.js b/test/bun.js/child_process-node.test.js
deleted file mode 100644
index f2c18c67c..000000000
--- a/test/bun.js/child_process-node.test.js
+++ /dev/null
@@ -1,482 +0,0 @@
-import { beforeAll, describe, expect, it } from "bun:test";
-import { ChildProcess, spawn, exec } from "node:child_process";
-import { throws, assert, createCallCheckCtx, createDoneDotAll } from "node-test-helpers";
-import { tmpdir } from "node:os";
-import { gcTick } from "gc";
-const strictEqual = (a, b) => expect(a).toStrictEqual(b);
-const debug = process.env.DEBUG ? console.log : () => {};
-
-const platformTmpDir = require("fs").realpathSync(tmpdir());
-
-const TYPE_ERR_NAME = "TypeError";
-
-console.log(process.cwd());
-
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-const common = {
- pwdCommand: ["pwd", []],
-};
-
-describe("ChildProcess.constructor", () => {
- it("should be a function", () => {
- strictEqual(typeof ChildProcess, "function");
- });
-});
-
-describe("ChildProcess.spawn()", () => {
- it("should throw on invalid options", () => {
- // Verify that invalid options to spawn() throw.
- const child = new ChildProcess();
-
- [undefined, null, "foo", 0, 1, NaN, true, false].forEach(options => {
- throws(
- () => {
- child.spawn(options);
- },
- {
- code: "ERR_INVALID_ARG_TYPE",
- name: TYPE_ERR_NAME,
- // message:
- // 'The "options" argument must be of type object.' +
- // `${common.invalidArgTypeHelper(options)}`,
- },
- );
- });
- });
-
- it("should throw if file is not a string", () => {
- // Verify that spawn throws if file is not a string.
- const child = new ChildProcess();
- [undefined, null, 0, 1, NaN, true, false, {}].forEach(file => {
- throws(
- () => {
- child.spawn({ file });
- },
- {
- code: "ERR_INVALID_ARG_TYPE",
- name: TYPE_ERR_NAME,
- // message:
- // 'The "options.file" property must be of type string.' +
- // `${common.invalidArgTypeHelper(file)}`,
- },
- );
- });
- });
-
- it("should throw if envPairs is not an array or undefined", () => {
- // Verify that spawn throws if envPairs is not an array or undefined.
- const child = new ChildProcess();
-
- [null, 0, 1, NaN, true, false, {}, "foo"].forEach(envPairs => {
- throws(
- () => {
- child.spawn({
- envPairs,
- stdio: ["ignore", "ignore", "ignore", "ipc"],
- });
- },
- {
- code: "ERR_INVALID_ARG_TYPE",
- name: TYPE_ERR_NAME,
- // message:
- // 'The "options.envPairs" property must be an instance of Array.' +
- // common.invalidArgTypeHelper(envPairs),
- },
- );
- });
- });
-
- it("should throw if stdio is not an array or undefined", () => {
- // Verify that spawn throws if args is not an array or undefined.
- const child = new ChildProcess();
-
- [null, 0, 1, NaN, true, false, {}, "foo"].forEach(args => {
- throws(
- () => {
- child.spawn({ file: "foo", args });
- },
- {
- code: "ERR_INVALID_ARG_TYPE",
- name: TYPE_ERR_NAME,
- // message:
- // 'The "options.args" property must be an instance of Array.' +
- // common.invalidArgTypeHelper(args),
- },
- );
- });
- });
-});
-
-describe("ChildProcess.spawn", () => {
- function getChild() {
- const child = new ChildProcess();
- child.spawn({
- file: "node",
- // file: process.execPath,
- args: ["node", "--interactive"],
- cwd: process.cwd(),
- stdio: ["ignore", "ignore", "ignore"],
- });
- return child;
- }
-
- it("should spawn a process", () => {
- const child = getChild();
- // Test that we can call spawn
-
- strictEqual(Object.hasOwn(child, "pid"), true);
- assert(Number.isInteger(child.pid));
- child.kill();
- });
-
- it("should throw error on invalid signal", () => {
- const child = getChild();
- // Try killing with invalid signal
- throws(
- () => {
- child.kill("foo");
- },
- { code: "ERR_UNKNOWN_SIGNAL", name: TYPE_ERR_NAME },
- );
- });
-});
-
-describe("ChildProcess spawn bad stdio", () => {
- // Monkey patch spawn() to create a child process normally, but destroy the
- // stdout and stderr streams. This replicates the conditions where the streams
- // cannot be properly created.
- function createChild(options, callback, done, target) {
- var __originalSpawn = ChildProcess.prototype.spawn;
- ChildProcess.prototype.spawn = function () {
- const err = __originalSpawn.apply(this, arguments);
-
- this.stdout.destroy();
- this.stderr.destroy();
-
- return err;
- };
-
- const { mustCall } = createCallCheckCtx(done);
- let cmd = `bun ${import.meta.dir}/spawned-child.js`;
- if (target) cmd += " " + target;
- const child = exec(cmd, options, mustCall(callback));
- ChildProcess.prototype.spawn = __originalSpawn;
- return child;
- }
-
- it("should handle normal execution of child process", done => {
- createChild(
- {},
- (err, stdout, stderr) => {
- strictEqual(err, null);
- strictEqual(stdout, "");
- strictEqual(stderr, "");
- },
- done,
- );
- });
-
- it("should handle error event of child process", done => {
- const error = new Error(`Command failed: bun ${import.meta.dir}/spawned-child.js ERROR`);
- createChild(
- {},
- (err, stdout, stderr) => {
- strictEqual(err.message, error.message);
- strictEqual(stdout, "");
- strictEqual(stderr, "");
- },
- done,
- "ERROR",
- );
- });
-
- it("should handle killed process", done => {
- createChild(
- { timeout: 1 },
- (err, stdout, stderr) => {
- strictEqual(err.killed, true);
- strictEqual(stdout, "");
- strictEqual(stderr, "");
- },
- done,
- );
- });
-});
-
-describe("child_process cwd", () => {
- // Spawns 'pwd' with given options, then test
- // - whether the child pid is undefined or number,
- // - whether the exit code equals expectCode,
- // - optionally whether the trimmed stdout result matches expectData
- function testCwd(options, { expectPidType, expectCode = 0, expectData }, done = () => {}) {
- const createDone = createDoneDotAll(done);
- const { mustCall } = createCallCheckCtx(createDone(1500));
- const exitDone = createDone(5000);
-
- const child = spawn(...common.pwdCommand, options);
-
- strictEqual(typeof child.pid, expectPidType);
-
- child.stdout.setEncoding("utf8");
-
- // No need to assert callback since `data` is asserted.
- let data = "";
- child.stdout.on("data", chunk => {
- data += chunk;
- });
-
- // TODO: Test exit events
- // // Can't assert callback, as stayed in to API:
- // // _The 'exit' event may or may not fire after an error has occurred._
- child.on("exit", (code, signal) => {
- try {
- strictEqual(code, expectCode);
- exitDone();
- } catch (err) {
- exitDone(err);
- }
- });
-
- child.on(
- "close",
- mustCall(() => {
- expectData && strictEqual(data.trim(), expectData);
- }),
- );
-
- return child;
- }
-
- // TODO: Make sure this isn't important
- // Currently Bun.spawn will still spawn even though cwd doesn't exist
- // // Assume does-not-exist doesn't exist, expect exitCode=-1 and errno=ENOENT
- // it("should throw an error when given cwd doesn't exist", () => {
- // testCwd({ cwd: "does-not-exist" }, "undefined", -1).on(
- // "error",
- // mustCall(function (e) {
- // console.log(e);
- // strictEqual(e.code, "ENOENT");
- // })
- // );
- // });
-
- // TODO: Make sure this isn't an important test
- // it("should throw when cwd is a non-file url", () => {
- // throws(() => {
- // testCwd(
- // {
- // cwd: new URL("http://example.com/"),
- // },
- // "number",
- // 0,
- // tmpdir.path
- // );
- // }, /The URL must be of scheme file/);
-
- // // if (process.platform !== "win32") {
- // // throws(() => {
- // // testCwd(
- // // {
- // // cwd: new URL("file://host/dev/null"),
- // // },
- // // "number",
- // // 0,
- // // tmpdir.path
- // // );
- // // }, /File URL host must be "localhost" or empty on/);
- // // }
- // });
-
- it("should work for valid given cwd", done => {
- const tmpdir = { path: platformTmpDir };
- const createDone = createDoneDotAll(done);
-
- // Assume these exist, and 'pwd' gives us the right directory back
- testCwd(
- { cwd: tmpdir.path },
- {
- expectPidType: "number",
- expectCode: 0,
- expectData: platformTmpDir,
- },
- createDone(1500),
- );
- const shouldExistDir = "/dev";
- testCwd(
- { cwd: shouldExistDir },
- {
- expectPidType: "number",
- expectCode: 0,
- expectData: shouldExistDir,
- },
- createDone(1500),
- );
- testCwd(
- { cwd: Bun.pathToFileURL(tmpdir.path) },
- {
- expectPidType: "number",
- expectCode: 0,
- expectData: platformTmpDir,
- },
- createDone(1500),
- );
- });
-
- it.skip("shouldn't try to chdir to an invalid cwd", done => {
- const createDone = createDoneDotAll(done);
- // Spawn() shouldn't try to chdir() to invalid arg, so this should just work
- testCwd({ cwd: "" }, { expectPidType: "number" }, createDone(1500));
- testCwd({ cwd: undefined }, { expectPidType: "number" }, createDone(1500));
- testCwd({ cwd: null }, { expectPidType: "number" }, createDone(1500));
- });
-});
-
-describe("child_process default options", () => {
- it("should use process.env as default env", done => {
- globalThis.process.env.TMPDIR = platformTmpDir;
-
- let child = spawn("printenv", [], {});
- let response = "";
-
- child.stdout.setEncoding("utf8");
- child.stdout.on("data", chunk => {
- debug(`stdout: ${chunk}`);
- response += chunk;
- });
-
- // NOTE: Original test used child.on("exit"), but this is unreliable
- // because the process can exit before the stream is closed and the data is read
- child.stdout.on("close", () => {
- expect(response.includes(`TMPDIR=${platformTmpDir}`)).toBe(true);
- done();
- });
- });
-});
-
-describe("child_process double pipe", () => {
- it("should allow two pipes to be used at once", done => {
- // const { mustCallAtLeast, mustCall } = createCallCheckCtx(done);
- const mustCallAtLeast = fn => fn;
- const mustCall = fn => fn;
- let grep, sed, echo;
- grep = spawn("grep", ["o"], { stdio: ["pipe", "pipe", "pipe"] });
- sed = spawn("sed", ["s/o/O/"]);
- echo = spawn("echo", ["hello\nnode\nand\nworld\n"]);
-
- // pipe grep | sed
- grep.stdout.on(
- "data",
- mustCallAtLeast(data => {
- debug(`grep stdout ${data.length}`);
- if (!sed.stdin.write(data)) {
- grep.stdout.pause();
- }
- }),
- );
-
- // print sed's output
- sed.stdout.on(
- "data",
- mustCallAtLeast(data => {
- result += data.toString("utf8");
- debug(data);
- }),
- );
-
- echo.stdout.on(
- "data",
- mustCallAtLeast(data => {
- debug(`grep stdin write ${data.length}`);
- if (!grep.stdin.write(data)) {
- debug("echo stdout pause");
- echo.stdout.pause();
- }
- }),
- );
-
- // TODO(Derrick): We don't implement the full API for this yet,
- // So stdin has no 'drain' event.
- // TODO(@jasnell): This does not appear to ever be
- // emitted. It's not clear if it is necessary.
- grep.stdin.on("drain", () => {
- debug("echo stdout resume");
- echo.stdout.resume();
- });
-
- // Propagate end from echo to grep
- echo.stdout.on(
- "end",
- mustCall(() => {
- debug("echo stdout end");
- grep.stdin.end();
- }),
- );
-
- echo.on(
- "exit",
- mustCall(() => {
- debug("echo exit");
- }),
- );
-
- grep.on(
- "exit",
- mustCall(() => {
- debug("grep exit");
- }),
- );
-
- sed.on(
- "exit",
- mustCall(() => {
- debug("sed exit");
- }),
- );
-
- // TODO(@jasnell): This does not appear to ever be
- // emitted. It's not clear if it is necessary.
- sed.stdin.on("drain", () => {
- grep.stdout.resume();
- });
-
- // Propagate end from grep to sed
- grep.stdout.on(
- "end",
- mustCall(() => {
- debug("grep stdout end");
- sed.stdin.end();
- }),
- );
-
- let result = "";
-
- sed.stdout.on(
- "end",
- mustCall(() => {
- debug("result: " + result);
- strictEqual(result, `hellO\nnOde\nwOrld\n`);
- done();
- }),
- );
- });
-});