mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
### What does this PR do? On Linux, AbortSignal.timeout created a file descriptor for each timeout and did not keep the event loop alive when a timer was active. This is fixed. ### How did you verify your code works? Fewer flaky tests --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Claude <claude@anthropic.ai>
91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { writeFileSync } from "fs";
|
|
import { bunEnv, bunExe, tmpdirSync } from "harness";
|
|
import { tmpdir } from "os";
|
|
import { join } from "path";
|
|
|
|
describe("AbortSignal", () => {
|
|
test("spawn test", async () => {
|
|
const fileName = `/abort.test.ts`;
|
|
const testFileContents = await Bun.file(join(import.meta.dir, "abort.ts")).arrayBuffer();
|
|
|
|
writeFileSync(join(tmpdirSync(), fileName), testFileContents, "utf8");
|
|
const { stderr } = Bun.spawnSync({
|
|
cmd: [bunExe(), "test", fileName],
|
|
env: bunEnv,
|
|
cwd: tmpdir(),
|
|
});
|
|
|
|
expect(stderr?.toString()).not.toContain("✗");
|
|
});
|
|
|
|
test("AbortSignal.timeout(n) should not freeze the process", async () => {
|
|
const fileName = join(import.meta.dir, "abort.signal.ts");
|
|
|
|
await using server = Bun.spawn({
|
|
cmd: [bunExe(), fileName],
|
|
env: bunEnv,
|
|
cwd: tmpdir(),
|
|
stdout: "inherit",
|
|
stderr: "inherit",
|
|
});
|
|
|
|
expect(await server.exited).toBe(0);
|
|
});
|
|
|
|
test("AbortSignal.any() should fire abort event", async () => {
|
|
async function testAny(signalToAbort: number) {
|
|
const { promise, resolve } = Promise.withResolvers();
|
|
|
|
const a = new AbortController();
|
|
const b = new AbortController();
|
|
// @ts-ignore
|
|
const signal = AbortSignal.any([a.signal, b.signal]);
|
|
const timeout = setTimeout(() => {
|
|
resolve(false);
|
|
}, 100);
|
|
|
|
signal.addEventListener("abort", () => {
|
|
clearTimeout(timeout);
|
|
resolve(true);
|
|
});
|
|
|
|
if (signalToAbort) {
|
|
b.abort();
|
|
} else {
|
|
a.abort();
|
|
}
|
|
|
|
expect(await promise).toBe(true);
|
|
expect(signal.aborted).toBe(true);
|
|
}
|
|
|
|
await testAny(0);
|
|
await testAny(1);
|
|
});
|
|
|
|
function fmt(value: any) {
|
|
const res = {};
|
|
for (const key in value) {
|
|
if (key === "column" || key === "line" || key === "sourceURL") continue;
|
|
res[key] = value[key];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
test(".signal.reason should be a DOMException", () => {
|
|
const ac = new AbortController();
|
|
ac.abort();
|
|
expect(ac.signal.reason).toBeInstanceOf(DOMException);
|
|
expect(fmt(ac.signal.reason)).toEqual(fmt(new DOMException("The operation was aborted.", "AbortError")));
|
|
expect(ac.signal.reason.code).toBe(20);
|
|
});
|
|
test(".signal.reason should be a DOMException for timeout", async () => {
|
|
const ac = AbortSignal.timeout(0);
|
|
await Bun.sleep(10);
|
|
expect(ac.reason).toBeInstanceOf(DOMException);
|
|
expect(fmt(ac.reason)).toEqual(fmt(new DOMException("The operation timed out.", "TimeoutError")));
|
|
expect(ac.reason.code).toBe(23);
|
|
});
|
|
});
|