Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion test/js/node/tty.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,15 @@ describe.concurrent("ReadStream destroy()", () => {

describe.concurrent("WriteStream destroy()", () => {
async function destroyStdioStreams(exhaustDescriptors: boolean) {
// ulimit keeps every descriptor of the child below this number.
const descriptorLimit = 64;
const script = `
const fs = require("fs");
const tty = require("tty");
const { once } = require("events");
const isOpen = fd => { try { fs.fstatSync(fd); return true; } catch { return false; } };
// With the limit, every descriptor that is open. Without it the list is empty: nothing below runs out of them.
const openFds = () => [...Array(${exhaustDescriptors ? descriptorLimit : 0}).keys()].filter(isOpen);
const hasSink = s => Object.getOwnPropertySymbols(s).some(sym => sym.description === "kWriteStreamFastPath");
const out = process.stdout;
const destroyed = [];
Expand All @@ -318,9 +322,17 @@ describe.concurrent("WriteStream destroy()", () => {
}
(async () => {
// A debug build opens each builtin module's file on first use, so load them while it still can.
const idle = openFds();
const warmup = new tty.WriteStream(1);
const sinkFds = openFds().filter(fd => !idle.includes(fd));
warmup.destroy();
await once(warmup, "close");
// On Linux the FileSink closes its descriptor on another thread, after "close". A close that
// came after the loop below would free a descriptor, and one of the streams would get a FileSink.
for (const deadline = performance.now() + 10_000; sinkFds.some(isOpen); ) {
if (performance.now() > deadline) throw new Error("open after 'close': fd " + sinkFds.filter(isOpen));
await new Promise(resolve => setImmediate(resolve));
}

const held = [];
let code;
Expand All @@ -344,7 +356,12 @@ describe.concurrent("WriteStream destroy()", () => {
})();
`;
await using proc = Bun.spawn({
cmd: [...(exhaustDescriptors ? ["sh", "-c", 'ulimit -n 64 && exec "$0" "$@"'] : []), bunExe(), "-e", script],
cmd: [
...(exhaustDescriptors ? ["sh", "-c", `ulimit -n ${descriptorLimit} && exec "$0" "$@"`] : []),
bunExe(),
"-e",
script,
],
stdin: Bun.file(import.meta.path),
stdout: "pipe",
stderr: "pipe",
Expand Down
Loading