diff options
author | 2023-03-07 12:22:34 -0800 | |
---|---|---|
committer | 2023-03-07 12:22:34 -0800 | |
commit | f7e4eb83694aa007a492ef66c28ffbe6a2dae791 (patch) | |
tree | 7af25aa5c42a2e1b2b47ba1df35f8caa9054cbeb /test/bun.js/child_process-node.test.js | |
parent | 36275a44ce7a33587bd26aad120042ab95470ff3 (diff) | |
download | bun-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.js | 482 |
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(); - }), - ); - }); -}); |