aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-11-23 07:09:32 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-11-23 07:09:32 -0800
commitc0ebef03e9ef143f0b9c3c5e4439794cd88a5230 (patch)
tree1ea75cf27f50a2afa54ce3883722fd2d338d5071
parent3282727dea3722cad8bcebd15390861b2938c7f8 (diff)
downloadbun-c0ebef03e9ef143f0b9c3c5e4439794cd88a5230.tar.gz
bun-c0ebef03e9ef143f0b9c3c5e4439794cd88a5230.tar.zst
bun-c0ebef03e9ef143f0b9c3c5e4439794cd88a5230.zip
seems to work!
-rw-r--r--src/bun.js/api/bun/spawn.zig4
-rw-r--r--src/bun.js/api/bun/subprocess.zig1
-rw-r--r--src/bun.js/base.zig8
-rw-r--r--src/bun.js/webcore/streams.zig5
-rw-r--r--test/bun.js/spawn.test.ts523
5 files changed, 277 insertions, 264 deletions
diff --git a/src/bun.js/api/bun/spawn.zig b/src/bun.js/api/bun/spawn.zig
index a009f2850..afcc5509b 100644
--- a/src/bun.js/api/bun/spawn.zig
+++ b/src/bun.js/api/bun/spawn.zig
@@ -206,8 +206,8 @@ pub const PosixSpawn = struct {
envp,
);
if (comptime bun.Environment.allow_assert)
- JSC.Node.Syscall.syslog("posix_spawn({s}, \"{s}\", \"{s}\") = {d} ({d})", .{
- path, std.mem.span(argv), std.mem.span(envp), rc, pid,
+ JSC.Node.Syscall.syslog("posix_spawn({s}) = {d} ({d})", .{
+ path, rc, pid,
});
if (comptime bun.Environment.isLinux) {
diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig
index 53bb3a8bc..c85e0396f 100644
--- a/src/bun.js/api/bun/subprocess.zig
+++ b/src/bun.js/api/bun/subprocess.zig
@@ -700,6 +700,7 @@ pub const Subprocess = struct {
.fd = fd,
.buffer = bun.ByteList.init(&.{}),
.allocator = globalThis.bunVM().allocator,
+ .auto_close = true,
};
if (other_fd != bun.invalid_fd) _ = JSC.Node.Syscall.close(other_fd);
sink.mode = std.os.S.IFIFO;
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index 5ac1dfa8e..981c0d2e4 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -4347,7 +4347,7 @@ pub const FilePoll = struct {
}
} else if (comptime Environment.isMac) {
var changelist = std.mem.zeroes([2]std.os.system.kevent64_s);
- const one_shot_flag: c_int = 0;
+ const one_shot_flag: u16 = if (!this.flags.contains(.one_shot)) 0 else std.c.EV_ONESHOT;
changelist[0] = switch (flag) {
.readable => .{
.ident = @intCast(u64, fd),
@@ -4355,7 +4355,7 @@ pub const FilePoll = struct {
.data = 0,
.fflags = 0,
.udata = @ptrToInt(Pollable.init(this).ptr()),
- .flags = std.c.EV_ADD | one_shot_flag | std.c.EV_RECEIPT | std.c.EV_CLEAR,
+ .flags = std.c.EV_ADD | one_shot_flag,
.ext = .{ 0, 0 },
},
.writable => .{
@@ -4364,7 +4364,7 @@ pub const FilePoll = struct {
.data = 0,
.fflags = 0,
.udata = @ptrToInt(Pollable.init(this).ptr()),
- .flags = std.c.EV_ADD | one_shot_flag | std.c.EV_RECEIPT | std.c.EV_CLEAR,
+ .flags = std.c.EV_ADD | one_shot_flag,
.ext = .{ 0, 0 },
},
.process => .{
@@ -4373,7 +4373,7 @@ pub const FilePoll = struct {
.data = 0,
.fflags = std.c.NOTE_EXIT,
.udata = @ptrToInt(Pollable.init(this).ptr()),
- .flags = std.c.EV_ADD | one_shot_flag | std.c.EV_RECEIPT | std.c.EV_CLEAR,
+ .flags = std.c.EV_ADD | one_shot_flag,
.ext = .{ 0, 0 },
},
else => unreachable,
diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig
index 6af7213f3..54819a4e5 100644
--- a/src/bun.js/webcore/streams.zig
+++ b/src/bun.js/webcore/streams.zig
@@ -1662,6 +1662,11 @@ pub const FileSink = struct {
std.debug.assert(this.next == null);
this.requested_end = true;
+ if (this.fd == bun.invalid_fd) {
+ this.cleanup();
+ return .{ .result = JSValue.jsNumber(this.written) };
+ }
+
const flushed = this.flush();
if (flushed == .err) {
diff --git a/test/bun.js/spawn.test.ts b/test/bun.js/spawn.test.ts
index d66687773..0959034bb 100644
--- a/test/bun.js/spawn.test.ts
+++ b/test/bun.js/spawn.test.ts
@@ -50,221 +50,221 @@ for (let [gcTick, label] of [
describe("spawn", () => {
const hugeString = "hello".repeat(10000).slice();
- // it("as an array", async () => {
- // gcTick();
- // await (async () => {
- // const { stdout } = spawn(["echo", "hello"], {
- // stdout: "pipe",
- // stderr: null,
- // stdin: null,
- // });
- // gcTick();
- // const text = await new Response(stdout).text();
- // expect(text).toBe("hello\n");
- // })();
- // gcTick();
- // });
+ it("as an array", async () => {
+ gcTick();
+ await (async () => {
+ const { stdout } = spawn(["echo", "hello"], {
+ stdout: "pipe",
+ stderr: null,
+ stdin: null,
+ });
+ gcTick();
+ const text = await new Response(stdout).text();
+ expect(text).toBe("hello\n");
+ })();
+ gcTick();
+ });
- // it("as an array with options object", async () => {
- // gcTick();
- // const { stdout } = spawn(["printenv", "FOO"], {
- // cwd: "/tmp",
- // env: {
- // ...process.env,
- // FOO: "bar",
- // },
- // stdin: null,
- // stdout: "pipe",
- // stderr: null,
- // });
- // gcTick();
- // const text = await new Response(stdout).text();
- // expect(text).toBe("bar\n");
- // gcTick();
- // });
+ it("as an array with options object", async () => {
+ gcTick();
+ const { stdout } = spawn(["printenv", "FOO"], {
+ cwd: "/tmp",
+ env: {
+ ...process.env,
+ FOO: "bar",
+ },
+ stdin: null,
+ stdout: "pipe",
+ stderr: null,
+ });
+ gcTick();
+ const text = await new Response(stdout).text();
+ expect(text).toBe("bar\n");
+ gcTick();
+ });
- // it("Uint8Array works as stdin", async () => {
- // rmSync("/tmp/out.123.txt", { force: true });
- // gcTick();
- // const { exited } = spawn({
- // cmd: ["cat"],
- // stdin: new TextEncoder().encode(hugeString),
- // stdout: Bun.file("/tmp/out.123.txt"),
- // });
- // gcTick();
- // await exited;
- // expect(require("fs").readFileSync("/tmp/out.123.txt", "utf8")).toBe(
- // hugeString,
- // );
- // gcTick();
- // });
+ it("Uint8Array works as stdin", async () => {
+ rmSync("/tmp/out.123.txt", { force: true });
+ gcTick();
+ const { exited } = spawn({
+ cmd: ["cat"],
+ stdin: new TextEncoder().encode(hugeString),
+ stdout: Bun.file("/tmp/out.123.txt"),
+ });
+ gcTick();
+ await exited;
+ expect(require("fs").readFileSync("/tmp/out.123.txt", "utf8")).toBe(
+ hugeString,
+ );
+ gcTick();
+ });
- // it("check exit code", async () => {
- // const exitCode1 = await spawn({
- // cmd: ["ls"],
- // }).exited;
- // gcTick();
- // const exitCode2 = await spawn({
- // cmd: ["false"],
- // }).exited;
- // gcTick();
- // expect(exitCode1).toBe(0);
- // expect(exitCode2).toBe(1);
- // gcTick();
- // });
+ it("check exit code", async () => {
+ const exitCode1 = await spawn({
+ cmd: ["ls"],
+ }).exited;
+ gcTick();
+ const exitCode2 = await spawn({
+ cmd: ["false"],
+ }).exited;
+ gcTick();
+ expect(exitCode1).toBe(0);
+ expect(exitCode2).toBe(1);
+ gcTick();
+ });
- // it("nothing to stdout and sleeping doesn't keep process open 4ever", async () => {
- // const proc = spawn({
- // cmd: ["sleep", "0.1"],
- // });
- // gcTick();
- // for await (const _ of proc.stdout!) {
- // throw new Error("should not happen");
- // }
- // gcTick();
- // });
+ it("nothing to stdout and sleeping doesn't keep process open 4ever", async () => {
+ const proc = spawn({
+ cmd: ["sleep", "0.1"],
+ });
+ gcTick();
+ for await (const _ of proc.stdout!) {
+ throw new Error("should not happen");
+ }
+ gcTick();
+ });
- // it("check exit code from onExit", async () => {
- // var exitCode1, exitCode2;
- // await new Promise<void>((resolve) => {
- // var counter = 0;
- // spawn({
- // cmd: ["ls"],
- // onExit(code) {
- // exitCode1 = code;
- // counter++;
- // if (counter === 2) {
- // resolve();
- // }
- // },
- // });
- // gcTick();
- // spawn({
- // cmd: ["false"],
- // onExit(code) {
- // exitCode2 = code;
- // counter++;
- // if (counter === 2) {
- // resolve();
- // }
- // },
- // });
- // gcTick();
- // });
- // gcTick();
- // expect(exitCode1).toBe(0);
- // expect(exitCode2).toBe(1);
- // gcTick();
- // });
+ it("check exit code from onExit", async () => {
+ var exitCode1, exitCode2;
+ await new Promise<void>((resolve) => {
+ var counter = 0;
+ spawn({
+ cmd: ["ls"],
+ onExit(code) {
+ exitCode1 = code;
+ counter++;
+ if (counter === 2) {
+ resolve();
+ }
+ },
+ });
+ gcTick();
+ spawn({
+ cmd: ["false"],
+ onExit(code) {
+ exitCode2 = code;
+ counter++;
+ if (counter === 2) {
+ resolve();
+ }
+ },
+ });
+ gcTick();
+ });
+ gcTick();
+ expect(exitCode1).toBe(0);
+ expect(exitCode2).toBe(1);
+ gcTick();
+ });
- // it("Blob works as stdin", async () => {
- // rmSync("/tmp/out.123.txt", { force: true });
- // gcTick();
- // const { exited } = spawn({
- // cmd: ["cat"],
- // stdin: new Blob([new TextEncoder().encode(hugeString)]),
- // stdout: Bun.file("/tmp/out.123.txt"),
- // });
-
- // await exited;
- // expect(await Bun.file("/tmp/out.123.txt").text()).toBe(hugeString);
- // });
+ it("Blob works as stdin", async () => {
+ rmSync("/tmp/out.123.txt", { force: true });
+ gcTick();
+ const { exited } = spawn({
+ cmd: ["cat"],
+ stdin: new Blob([new TextEncoder().encode(hugeString)]),
+ stdout: Bun.file("/tmp/out.123.txt"),
+ });
- // it("Bun.file() works as stdout", async () => {
- // rmSync("/tmp/out.123.txt", { force: true });
- // gcTick();
- // const { exited } = spawn({
- // cmd: ["echo", "hello"],
- // stdout: Bun.file("/tmp/out.123.txt"),
- // });
-
- // await exited;
- // gcTick();
- // expect(await Bun.file("/tmp/out.123.txt").text()).toBe("hello\n");
- // });
+ await exited;
+ expect(await Bun.file("/tmp/out.123.txt").text()).toBe(hugeString);
+ });
- // it("Bun.file() works as stdin", async () => {
- // await write(Bun.file("/tmp/out.456.txt"), "hello there!");
- // gcTick();
- // const { stdout } = spawn({
- // cmd: ["cat"],
- // stdout: "pipe",
- // stdin: Bun.file("/tmp/out.456.txt"),
- // });
- // gcTick();
- // expect(await readableStreamToText(stdout!)).toBe("hello there!");
- // });
+ it("Bun.file() works as stdout", async () => {
+ rmSync("/tmp/out.123.txt", { force: true });
+ gcTick();
+ const { exited } = spawn({
+ cmd: ["echo", "hello"],
+ stdout: Bun.file("/tmp/out.123.txt"),
+ });
- // it("Bun.file() works as stdin and stdout", async () => {
- // writeFileSync("/tmp/out.456.txt", "hello!");
- // gcTick();
- // writeFileSync("/tmp/out.123.txt", "wrong!");
- // gcTick();
-
- // const { exited } = spawn({
- // cmd: ["cat"],
- // stdout: Bun.file("/tmp/out.123.txt"),
- // stdin: Bun.file("/tmp/out.456.txt"),
- // });
- // gcTick();
- // await exited;
- // expect(await Bun.file("/tmp/out.456.txt").text()).toBe("hello!");
- // gcTick();
- // expect(await Bun.file("/tmp/out.123.txt").text()).toBe("hello!");
- // });
+ await exited;
+ gcTick();
+ expect(await Bun.file("/tmp/out.123.txt").text()).toBe("hello\n");
+ });
- // it("stdout can be read", async () => {
- // await Bun.write("/tmp/out.txt", hugeString);
- // gcTick();
- // const { stdout } = spawn({
- // cmd: ["cat", "/tmp/out.txt"],
- // stdout: "pipe",
- // });
+ it("Bun.file() works as stdin", async () => {
+ await write(Bun.file("/tmp/out.456.txt"), "hello there!");
+ gcTick();
+ const { stdout } = spawn({
+ cmd: ["cat"],
+ stdout: "pipe",
+ stdin: Bun.file("/tmp/out.456.txt"),
+ });
+ gcTick();
+ expect(await readableStreamToText(stdout!)).toBe("hello there!");
+ });
- // gcTick();
+ it("Bun.file() works as stdin and stdout", async () => {
+ writeFileSync("/tmp/out.456.txt", "hello!");
+ gcTick();
+ writeFileSync("/tmp/out.123.txt", "wrong!");
+ gcTick();
- // const text = await readableStreamToText(stdout!);
- // gcTick();
- // expect(text).toBe(hugeString);
- // });
+ const { exited } = spawn({
+ cmd: ["cat"],
+ stdout: Bun.file("/tmp/out.123.txt"),
+ stdin: Bun.file("/tmp/out.456.txt"),
+ });
+ gcTick();
+ await exited;
+ expect(await Bun.file("/tmp/out.456.txt").text()).toBe("hello!");
+ gcTick();
+ expect(await Bun.file("/tmp/out.123.txt").text()).toBe("hello!");
+ });
- // it("kill(1) works", async () => {
- // const process = spawn({
- // cmd: ["bash", "-c", "sleep 1000"],
- // stdout: "pipe",
- // });
- // gcTick();
- // const prom = process.exited;
- // process.kill(1);
- // await prom;
- // });
+ it("stdout can be read", async () => {
+ await Bun.write("/tmp/out.txt", hugeString);
+ gcTick();
+ const { stdout } = spawn({
+ cmd: ["cat", "/tmp/out.txt"],
+ stdout: "pipe",
+ });
- // it("kill() works", async () => {
- // const process = spawn({
- // cmd: ["bash", "-c", "sleep 1000"],
- // stdout: "pipe",
- // });
- // gcTick();
- // const prom = process.exited;
- // process.kill();
- // await prom;
- // });
+ gcTick();
+
+ const text = await readableStreamToText(stdout!);
+ gcTick();
+ expect(text).toBe(hugeString);
+ });
+
+ it("kill(1) works", async () => {
+ const process = spawn({
+ cmd: ["bash", "-c", "sleep 1000"],
+ stdout: "pipe",
+ });
+ gcTick();
+ const prom = process.exited;
+ process.kill(1);
+ await prom;
+ });
- it.only("stdin can be read and stdout can be written", async () => {
+ it("kill() works", async () => {
+ const process = spawn({
+ cmd: ["bash", "-c", "sleep 1000"],
+ stdout: "pipe",
+ });
+ gcTick();
+ const prom = process.exited;
+ process.kill();
+ await prom;
+ });
+
+ it("stdin can be read and stdout can be written", async () => {
const proc = spawn({
cmd: ["bash", import.meta.dir + "/bash-echo.sh"],
stdout: "pipe",
stdin: "pipe",
+ lazy: true,
stderr: "inherit",
});
- proc.stdin!.write(hugeString);
- console.log("wait4end");
- await proc.stdin!.flush();
- console.log("end");
- var text = "";
var stdout = proc.stdout!;
var reader = stdout.getReader();
+ proc.stdin!.write("hey\n");
+ await proc.stdin!.end();
+ var text = "";
+
reader;
var done = false,
value;
@@ -280,79 +280,86 @@ for (let [gcTick, label] of [
}
}
- expect(text.trim().length).toBe(hugeString.length);
- expect(text.trim()).toBe(hugeString);
+ expect(text.trim().length).toBe("hey".length);
+ expect(text.trim()).toBe("hey");
gcTick();
await proc.exited;
});
- describe("pipe", () => {
- function huge() {
- return spawn({
- cmd: ["echo", hugeString],
- stdout: "pipe",
- stdin: "pipe",
- stderr: "inherit",
- });
- }
+ // describe("pipe", () => {
+ // function huge() {
+ // return spawn({
+ // cmd: ["echo", hugeString],
+ // stdout: "pipe",
+ // stdin: "pipe",
+ // stderr: "inherit",
+ // lazy: true,
+ // });
+ // }
- function helloWorld() {
- return spawn({
- cmd: ["echo", "hello"],
- stdout: "pipe",
- stdin: "pipe",
- });
- }
+ // function helloWorld() {
+ // return spawn({
+ // cmd: ["echo", "hello"],
+ // stdout: "pipe",
+ // stdin: "ignore",
+ // });
+ // }
- const fixtures = [
- [helloWorld, "hello"],
- [huge, hugeString],
- ] as const;
-
- for (const [callback, fixture] of fixtures) {
- describe(fixture.slice(0, 12), () => {
- describe("should should allow reading stdout", () => {
- it("before exit", async () => {
- const process = callback();
- const output = await readableStreamToText(process.stdout!);
- const expected = fixture + "\n";
- expect(output.length).toBe(expected.length);
- expect(output).toBe(expected);
-
- await process.exited;
- });
-
- it("before exit (chunked)", async () => {
- const process = callback();
- var output = "";
-
- for await (const chunk of process.stdout) {
- output += new TextDecoder().decode(chunk);
- }
- console.log(output);
- const expected = fixture + "\n";
- expect(output.length).toBe(expected.length);
- expect(output).toBe(expected);
-
- await process.exited;
- });
-
- it("after exit", async () => {
- const process = callback();
- await process.stdin!.end();
-
- const output = await readableStreamToText(process.stdout!);
- const expected = fixture + "\n";
-
- expect(output.length).toBe(expected.length);
- expect(output).toBe(expected);
-
- await process.exited;
- });
- });
- });
- }
- });
+ // const fixtures = [
+ // [helloWorld, "hello"],
+ // [huge, hugeString],
+ // ] as const;
+
+ // for (const [callback, fixture] of fixtures) {
+ // describe(fixture.slice(0, 12), () => {
+ // describe("should should allow reading stdout", () => {
+ // it("before exit", async () => {
+ // const process = callback();
+ // const output = readableStreamToText(process.stdout!);
+ // const expected = fixture + "\n";
+
+ // await Promise.all([
+ // process.exited,
+ // output.then((output) => {
+ // expect(output.length).toBe(expected.length);
+ // expect(output).toBe(expected);
+ // }),
+ // ]);
+ // });
+
+ // it("before exit (chunked)", async () => {
+ // const process = callback();
+ // var output = "";
+ // const prom2 = (async function () {
+ // for await (const chunk of process.stdout) {
+ // output += new TextDecoder().decode(chunk);
+ // }
+ // })();
+
+ // const expected = fixture + "\n";
+
+ // await Promise.all([process.exited, prom2]);
+ // expect(output.length).toBe(expected.length);
+ // expect(output).toBe(expected);
+ // });
+
+ // it("after exit", async () => {
+ // const process = callback();
+
+ // const output = readableStreamToText(process.stdout!);
+ // const expected = fixture + "\n";
+ // await Promise.all([
+ // process.exited,
+ // output.then((output) => {
+ // expect(output.length).toBe(expected.length);
+ // expect(output).toBe(expected);
+ // }),
+ // ]);
+ // });
+ // });
+ // });
+ // }
+ // });
});
});
}