Compare commits

...

2 Commits

Author SHA1 Message Date
pfg
c4ed0466c8 switch to bun.Output.buffered_stdin and add more tests for alert/prompt/confirm 2025-02-19 17:13:38 -08:00
pfg
a46459e85b read stdin from bun.sys.File 2025-02-19 17:13:38 -08:00
3 changed files with 126 additions and 7 deletions

View File

@@ -62,8 +62,7 @@ fn alert(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSErr
bun.Output.flush();
// 7. Optionally, pause while waiting for the user to acknowledge the message.
var stdin = std.io.getStdIn();
var reader = stdin.reader();
var reader = bun.Output.buffered_stdin.reader();
while (true) {
const byte = reader.readByte() catch break;
if (byte == '\n') break;
@@ -110,10 +109,7 @@ fn confirm(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSE
bun.Output.flush();
// 6. Pause until the user responds either positively or negatively.
var stdin = std.io.getStdIn();
const unbuffered_reader = stdin.reader();
var buffered = std.io.bufferedReader(unbuffered_reader);
var reader = buffered.reader();
var reader = bun.Output.buffered_stdin.reader();
const first_byte = reader.readByte() catch {
return .false;

View File

@@ -1432,7 +1432,8 @@ pub const RunCommand = struct {
var list = std.ArrayList(u8).init(stack_fallback.get());
errdefer list.deinit();
std.io.getStdIn().reader().readAllArrayList(&list, 1024 * 1024 * 1024) catch return false;
bun.Output.buffered_stdin.reader().readAllArrayList(&list, 1024 * 1024 * 1024) catch return false;
ctx.runtime_options.eval.script = list.items;
const trigger = bun.pathLiteral("/[stdin]");

View File

@@ -0,0 +1,122 @@
import { bunEnv, bunExe } from "harness";
import { $ } from "bun";
const commands: [js: string, stdout_before: string, tests: [input: string | undefined, stdout_after: string][]][] = [
[
`alert("AlertMsg")`,
"AlertMsg [Enter] ",
[
["\n", "undefined\n"],
["abc\n", "undefined\n"],
["hello\r\n", "undefined\n"],
[undefined, "undefined\n"],
],
],
[
`prompt("PromptMsg")`,
"PromptMsg ",
[
["\n", "null\n"],
["abc\n", "abc\n"],
["hello\r\n", "hello\n"],
[undefined, "null\n"],
],
],
[
`confirm("ConfirmMsg")`,
"ConfirmMsg [y/N] ",
[
["\n", "false\n"],
["y\n", "true\n"],
["n\n", "false\n"],
["whatever\n", "false\n"],
["Y\n", "true\n"],
["Y\r\n", "true\n"],
["yes\n", "false\n"],
["yeah no\n", "false\n"],
[undefined, "false\n"],
],
],
];
for (const [js, stdout_before, tests] of commands) {
describe(js, () => {
for (const [input, stdout_after] of tests) {
it(`${JSON.stringify(input)} -> ${JSON.stringify(stdout_after)}`, async () => {
const result = await Bun.spawn({
cmd: [bunExe(), "-e", "console.log(" + js + ")"],
stdio: ["pipe", "pipe", "inherit"],
env: bunEnv,
});
const reader = result.stdout.getReader();
await expectStdout(reader, stdout_before);
if (input) {
result.stdin.write(input);
} else {
await result.stdin.end();
}
await expectStdout(reader, stdout_after);
await result.exited;
expect(result.exitCode).toBe(0);
});
}
});
}
const all_at_once_script = commands
.flatMap(([js, stdout_before, tests]) =>
tests.filter(t => t[0] != null).map(([input, stdout_after]) => `console.log(${js});await Bun.sleep(0);`),
)
.join("\n");
async function expectStdout(reader: ReadableStreamDefaultReader<Uint8Array>, value: string) {
let chunk_1 = await reader.read();
expect(chunk_1.done).toBe(false);
let dec = new TextDecoder().decode(chunk_1.value);
while (value.startsWith(dec)) {
value = value.substring(dec.length);
if (value.length === 0) break;
chunk_1 = await reader.read();
expect(chunk_1.done).toBe(false);
dec = new TextDecoder().decode(chunk_1.value);
}
}
test("all at once", async () => {
const result = await Bun.spawn({
cmd: [bunExe(), "-e", all_at_once_script],
stdio: ["pipe", "pipe", "inherit"],
env: bunEnv,
});
const reader = result.stdout.getReader();
for (const [js, stdout_before, tests] of commands) {
for (const [input, stdout_after] of tests) {
if (input == null) continue;
await expectStdout(reader, stdout_before);
if (input) {
result.stdin.write(input);
} else {
await result.stdin.end();
}
await expectStdout(reader, stdout_after);
}
}
await result.exited;
expect(result.exitCode).toBe(0);
});
test("all at once, from bun shell", async () => {
let stdin = commands
.flatMap(([js, stdout_before, tests]) => tests.filter(t => t[0] != null).map(([input, stdout_after]) => input))
.join("");
const result = await $`echo ${stdin} | ${bunExe()} -e ${all_at_once_script}`.text();
expect(result.split("\n")).toStrictEqual(
commands
.flatMap(([js, stdout_before, tests]) =>
tests.filter(t => t[0] != null).map(([input, stdout_after]) => stdout_before + stdout_after),
)
.join("")
.split("\n"),
);
});