import { describe, it, expect } from "bun:test"; import { ChildProcess, spawn, execFile, exec, fork, spawnSync, execFileSync, execSync } from "node:child_process"; import { tmpdir } from "node:os"; import { promisify } from "node:util"; const debug = process.env.DEBUG ? console.log : () => {}; const platformTmpDir = require("fs").realpathSync(tmpdir()); // Semver regex: https://gist.github.com/jhorsman/62eeea161a13b80e39f5249281e17c39?permalink_comment_id=2896416#gistcomment-2896416 // Not 100% accurate, but good enough for this test const SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-[a-zA-Z\d][-a-zA-Z.\d]*)?(\+[a-zA-Z\d][-a-zA-Z.\d]*)?$/; describe("ChildProcess.spawn()", () => { it("should emit `spawn` on spawn", async () => { const proc = new ChildProcess(); const result = await new Promise(resolve => { proc.on("spawn", () => { resolve(true); }); // @ts-ignore proc.spawn({ file: "bun", args: ["bun", "-v"] }); }); expect(result).toBe(true); }); it("should emit `exit` when killed", async () => { const proc = new ChildProcess(); const result = await new Promise(resolve => { proc.on("exit", () => { resolve(true); }); // @ts-ignore proc.spawn({ file: "bun", args: ["bun", "-v"] }); proc.kill(); }); expect(result).toBe(true); }); }); describe("spawn()", () => { it("should spawn a process", () => { const child = spawn("echo", ["hello"]); expect(!!child).toBe(true); }); it("should disallow invalid filename", () => { let child; let child2; try { // @ts-ignore child = spawn(123); // @ts-ignore child2 = spawn(["echo", "hello"]); } catch (e) {} expect(!!child).toBe(false); expect(!!child2).toBe(false); }); it.todo("should allow stdout to be read via Node stream.Readable `data` events", async () => { const child = spawn("bun", ["-v"]); const result: string = await new Promise(resolve => { child.stdout.on("error", e => { console.error(e); }); child.stdout.on("data", data => { debug(`stdout: ${data}`); resolve(data.toString()); }); child.stderr.on("data", data => { debug(`stderr: ${data}`); }); }); expect(SEMVER_REGEX.test(result.trim())).toBe(true); }); it.todo("should allow stdout to be read via .read() API", async () => { const child = spawn("bun", ["-v"]); const result: string = await new Promise((resolve, reject) => { let finalData = ""; child.stdout.on("error", e => { reject(e); }); child.stdout.on("readable", () => { let data; while ((data = child.stdout.read()) !== null) { finalData += data.toString(); } resolve(finalData); }); }); expect(SEMVER_REGEX.test(result.trim())).toBe(true); }); it("should accept stdio option with 'ignore' for no stdio fds", async () => { const child1 = spawn("bun", ["-v"], { stdio: "ignore", }); const child2 = spawn("bun", ["-v"], { stdio: ["ignore", "ignore", "ignore"], }); expect(!!child1).toBe(true); expect(child1.stdin).toBe(null); expect(child1.stdout).toBe(null); expect(child1.stderr).toBe(null); expect(!!child2).toBe(true); expect(child2.stdin).toBe(null); expect(child2.stdout).toBe(null); expect(child2.stderr).toBe(null); }); it("should allow us to set cwd", async () => { const child = spawn("pwd", { cwd: platformTmpDir }); const result: string = await new Promise(resolve => { child.stdout.on("data", data => { resolve(data.toString()); }); }); expect(result.trim()).toBe(platformTmpDir); }); it("should allow us to write to stdin", async () => { const child = spawn("tee"); const result: string = await new Promise(resolve => { child.stdin.write("hello"); child.stdout.on("data", data => { resolve(data.toString()); }); }); expect(result.trim()).toBe("hello"); }); it("should allow us to timeout hanging processes", async () => { const child = spawn("sleep", ["2"], { timeout: 3 }); const start = performance.now(); let end: number; await new Promise(resolve => { child.on("exit", () => { end = performance.now(); resolve(true); }); }); expect(end!).toBeDefined(); expect(end! - start < 2000).toBe(true); }); it("should allow us to set env", async () => { const child = spawn("env", { env: { TEST: "test" } }); const result: string = await new Promise(resolve => { child.stdout.on("data", data => { resolve(data.toString()); }); }); expect(/TEST\=test/.test(result)).toBe(true); }); it("should allow explicit setting of argv0", async () => { var resolve: (_?: any) => void; const promise = new Promise(resolve1 => { resolve = resolve1; }); process.env.NO_COLOR = "1"; const child = spawn("node", ["--help"], { argv0: "bun" }); delete process.env.NO_COLOR; let msg = ""; child.stdout.on("data", data => { msg += data.toString(); }); child.stdout.on("close", () => { resolve(msg); }); const result = await promise; expect(/Open bun's Discord server/.test(result)).toBe(true); }); it("should allow us to spawn in a shell", async () => { const result1: string = await new Promise(resolve => { const child1 = spawn("echo", ["$0"], { shell: true }); child1.stdout.on("data", data => { resolve(data.toString()); }); }); const result2: string = await new Promise(resolve => { const child2 = spawn("echo", ["$0"], { shell: "bash" }); child2.stdout.on("data", data => { resolve(data.toString()); }); }); expect(result1.trim()).toBe(Bun.which("sh")); expect(result2.trim()).toBe(Bun.which("bash")); }); it("should spawn a process synchronously", () => { const { stdout } = spawnSync("echo", ["hello"], { encoding: "utf8" }); expect(stdout.trim()).toBe("hello"); }); }); describe("execFile()", () => { it.todo("should execute a file", async () => { const result: Buffer = await new Promise((resolve, reject) => { execFile("bun", ["-v"], { encoding: "buffer" }, (error, stdout, stderr) => { if (error) { reject(error); } resolve(stdout); }); }); expect(SEMVER_REGEX.test(result.toString().trim())).toBe(true); }); }); describe("exec()", () => { it.todo("should execute a command in a shell", async () => { const result: Buffer = await new Promise((resolve, reject) => { exec("bun -v", { encoding: "buffer" }, (error, stdout, stderr) => { if (error) { reject(error); } resolve(stdout); }); }); expect(SEMVER_REGEX.test(result.toString().trim())).toBe(true); }); it.todo("should return an object w/ stdout and stderr when promisified", async () => { const result = await promisify(exec)("bun -v"); expect(typeof result).toBe("object"); expect(typeof result.stdout).toBe("string"); expect(typeof result.stderr).toBe("string"); const { stdout, stderr } = result; expect(SEMVER_REGEX.test(stdout.trim())).toBe(true); expect(stderr.trim()).toBe(""); }); }); describe("fork()", () => { it("should throw an error when used", () => { let err; try { fork("index.js"); } catch (e) { err = e; } expect(err instanceof Error).toBe(true); }); }); describe("spawnSync()", () => { it("should spawn a process synchronously", () => { const { stdout } = spawnSync("echo", ["hello"], { encoding: "utf8" }); expect(stdout.trim()).toBe("hello"); }); }); describe("execFileSync()", () => { it.todo("should execute a file synchronously", () => { const result = execFileSync("bun", ["-v"], { encoding: "utf8" }); expect(SEMVER_REGEX.test(result.trim())).toBe(true); }); it("should allow us to pass input to the command", () => { const result = execFileSync("node", [import.meta.dir + "/spawned-child.js", "STDIN"], { input: "hello world!", encoding: "utf8", }); expect(result.trim()).toBe("data: hello world!"); }); }); describe("execSync()", () => { it.todo("should execute a command in the shell synchronously", () => { const result = execSync("bun -v", { encoding: "utf8" }); expect(SEMVER_REGEX.test(result.trim())).toBe(true); }); }); describe("Bun.spawn()", () => { it("should return exit code 0 on successful execution", async () => { const proc = Bun.spawn({ cmd: ["echo", "hello"], stdout: "pipe", }); for await (const chunk of proc.stdout) { const text = new TextDecoder().decode(chunk); expect(text.trim()).toBe("hello"); } const result = await new Promise(resolve => { const maybeExited = Bun.peek(proc.exited); if (maybeExited === proc.exited) { proc.exited.then(code => resolve(code)); } else { resolve(maybeExited); } }); expect(result).toBe(0); }); // it("should fail when given an invalid cwd", () => { // const child = Bun.spawn({ cmd: ["echo", "hello"], cwd: "/invalid" }); // expect(child.pid).toBe(undefined); // }); }); ly-2732'>jarred/possibly-2732 Unnamed repository; edit this file 'description' to name the repository.
aboutsummaryrefslogtreecommitdiff
path: root/src/analytics (unfollow)
AgeCommit message (Collapse)AuthorFilesLines
2023-08-18Fix `make headers`Gravatar Jarred Sumner 7-58/+31
2023-08-18prettierGravatar Jarred Sumner 1-14/+14
2023-08-18feat: add self-closing & can-have-content (#4206)Gravatar Brúnó Salomon 7-26/+147
2023-08-18Implement BigIntStats (#4208)Gravatar dave caruso 14-221/+1874
* Implement BigIntStats * changes * rename test * comment * test changes?
2023-08-18Remove most C API usages, add debugger pretty printers for `Headers`, ↵Gravatar Jarred Sumner 72-8964/+7324
`URLSearchParams`, `FormData`, `Worker`, `EventTarget` (#4187) * Add pretty printers for `Headers`, `URLSearchParams`, and `FormData` * [untested] Add way to code generate getInternalProperties * bump * Bump Webkit * Ref the event loop while loaded * wip * checkpoint * another checkpoint * The code has been written * Fixup exports * Fix all the errors * Fix bug * [console.log] Fix bug when printing non-reified types missing values * Fix loading hash table * fix plugin * Fix ref & unref * auto-unref * various fixes * Update bun.zig * Set toStringTag * Delete code for macro JSX * Delete code for `bun dev` HTTP JS * Move Bun.serve to C++ API * Delete JSC C API code * :scissors: :skull: code * Use JSC C++ for `confirm`, `Crypto`, `prompt`, `alert` * more dead code * Update exports.zig * Use JSC C++ API for FFI * Remove remaining usages * Remove remaining usages * Update ffi.ts * Update InternalModuleRegistryConstants.h * draw the rest of the owl * Update webcore.zig * bind it * Fix performance regression in crypto.randomUIUD() * Update js_parser.zig --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-08-18fix `--bail` testGravatar Dylan Conway 1-1/+1
2023-08-17Add `util.inspect.custom` support to `util.inspect/Bun.inspect/console.log` ↵Gravatar dave caruso 15-78/+648
(#4194) * start work on util.inspect.custom * asdf * finish util inspect custom inspect * inspect * fix tests * revert * tidy * revert * oops * test * fix issues
2023-08-17Support Nitro (#4098)Gravatar dave caruso 10-18/+180
* Add formatWithOptions * tests and tweaks * adjust * changes * hi * add mark/measure stubs * stuff * allow unix absolute paths here * typo * rebase * fix stats
2023-08-17Update Astro guideGravatar Colin McDonnell 1-1/+5
2023-08-17Allow IncomingRequest.req to be overwritten. (#4154)Gravatar dave caruso 3-8/+21
* Allow IncomingRequest.req to be overwritten. * add test * fix test * yoo
2023-08-17Fix(node:fs): add buffer parameter in fs.read callback. (#4191)Gravatar Ai Hoshino 3-5/+151
Close: #4178
2023-08-17refactor: move HTMLRewriter to c++ bindings (#4193)Gravatar Brúnó Salomon 16-1127/+4961
2023-08-17Fix description for executables pageGravatar Colin McDonnell 1-1/+1