/// // The fake-clock harness fires every zero-delay timer from one `kick()`, in // registration order, in a single real task. Two properties of that batch are // load-bearing for code that takes a turn of the event loop without waiting on // a timer — the memory transport's frame pump and the memory server's // subscription refresh both do, through `armTurn`, because waking Deno's loop // for a timer costs milliseconds. // // A `setImmediate` has to be one of the batch. Left to the real event loop it // would run ahead of every turn the harness was holding, and no `settle()` or // `tick()` could hold it back. // // And clearing has to work inside the batch. `armTurn` arms both a timer and // an immediate for one turn and cancels the loser from the winner's callback, // so a batch that fired a cleared callback anyway would run the handler twice. import { describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; // The harness installs its replacements around each test, so these read the // global when they are called rather than when this module loads. const host = globalThis as unknown as { setImmediate: (handler: () => void) => unknown; clearImmediate: (handle: unknown) => void; }; const setImmediate = (handler: () => void): unknown => host.setImmediate(handler); const clearImmediate = (handle: unknown): void => host.clearImmediate(handle); describe("fake-clock zero-delay batch", () => { it("runs a setImmediate in registration order among the zero-delay timers", async () => { const order: string[] = []; setTimeout(() => order.push("timer-before"), 0); setImmediate(() => order.push("immediate")); setTimeout(() => order.push("timer-after"), 0); await clock.settle(); expect(order).toEqual(["timer-before", "immediate", "timer-after"]); }); it("does not run a zero-delay timer cleared by an earlier one in the batch", async () => { const fired: string[] = []; // The first timer is armed first so it fires first; the one it clears is // still ahead of it in the same batch. const armed: { handle?: ReturnType } = {}; setTimeout(() => { fired.push("first"); clearTimeout(armed.handle); }, 0); armed.handle = setTimeout(() => fired.push("second"), 0); await clock.settle(); expect(fired).toEqual(["first"]); }); it("does not run a setImmediate cleared by an earlier timer in the batch", async () => { const fired: string[] = []; // The timer is armed first so it fires first; the immediate it clears is // still ahead of it in the same batch. const armed: { handle?: unknown } = {}; setTimeout(() => { fired.push("timer"); clearImmediate(armed.handle); }, 0); armed.handle = setImmediate(() => fired.push("immediate")); await clock.settle(); expect(fired).toEqual(["timer"]); }); });