Files
bun.sh/test/js/bun/spawn/spawnsync-isolated-event-loop.test.ts
2025-11-07 05:28:33 -08:00

162 lines
4.3 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
describe.concurrent("spawnSync isolated event loop", () => {
test("JavaScript timers should not fire during spawnSync", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
"-e",
`
let timerFired = false;
// Set a timer that should NOT fire during spawnSync
const interval = setInterval(() => {
timerFired = true;
console.log("TIMER_FIRED");
process.exit(1);
}, 1);
// Run a subprocess synchronously
const result = Bun.spawnSync({
cmd: ["${bunExe()}", "-e", "Bun.sleepSync(16)"],
env: process.env,
});
clearInterval(interval);
console.log("SUCCESS: Timer did not fire during spawnSync");
process.exit(0);
`,
],
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
expect(stdout).toContain("SUCCESS");
expect(stdout).not.toContain("TIMER_FIRED");
expect(stdout).not.toContain("FAIL");
expect(exitCode).toBe(0);
});
test("microtasks should not drain during spawnSync", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
"-e",
`
queueMicrotask(() => {
console.log("MICROTASK_FIRED");
process.exit(1);
});
// Run a subprocess synchronously
const result = Bun.spawnSync({
cmd: ["${bunExe()}", "-e", "42"],
env: process.env,
});
console.log("SUCCESS: Timer did not fire during spawnSync");
process.exit(0);
`,
],
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
expect(stdout).toContain("SUCCESS");
expect(stdout).not.toContain("MICROTASK_FIRED");
expect(stdout).not.toContain("FAIL");
expect(exitCode).toBe(0);
});
test("stdin/stdout from main process should not be affected by spawnSync", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
"-e",
`
// Write to stdout before spawnSync
console.log("BEFORE");
// Run a subprocess synchronously
const result = Bun.spawnSync({
cmd: ["echo", "SUBPROCESS"],
env: process.env,
});
// Write to stdout after spawnSync
console.log("AFTER");
// Verify subprocess output
const subprocessOut = new TextDecoder().decode(result.stdout);
if (!subprocessOut.includes("SUBPROCESS")) {
console.log("FAIL: Subprocess output missing");
process.exit(1);
}
console.log("SUCCESS");
process.exit(0);
`,
],
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
expect(stdout).toContain("BEFORE");
expect(stdout).toContain("AFTER");
expect(stdout).toContain("SUCCESS");
expect(exitCode).toBe(0);
});
test("multiple spawnSync calls should each use isolated event loop", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
"-e",
`
let timerCount = 0;
// Set timers that should NOT fire during spawnSync
setTimeout(() => { timerCount++; }, 10);
setTimeout(() => { timerCount++; }, 20);
setTimeout(() => { timerCount++; }, 30);
// Run multiple subprocesses synchronously
for (let i = 0; i < 3; i++) {
const result = Bun.spawnSync({
cmd: ["${bunExe()}", "-e", "Bun.sleepSync(50)"],
});
if (timerCount > 0) {
console.log(\`FAIL: Timer fired during spawnSync iteration \${i}\`);
process.exit(1);
}
}
console.log("SUCCESS: No timers fired during any spawnSync call");
process.exit();
`,
],
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
expect(stdout).toContain("SUCCESS");
expect(stdout).not.toContain("FAIL");
expect(exitCode).toBe(0);
});
});