import { describe, expect, it } from "vitest"; import { startCappedFrameLoop } from "./cappedFrameLoopRuntime"; function createFrameQueue() { const callbacks = new Map void>(); const cancelled: number[] = []; let nextFrame = 1; return { callbacks, cancelled, requestFrame(callback: (timestampMs: number) => void) { const frame = nextFrame; nextFrame += 1; callbacks.set(frame, callback); return frame; }, cancelFrame(frame: number) { cancelled.push(frame); callbacks.delete(frame); }, run(frame: number, timestampMs: number) { const callback = callbacks.get(frame); callbacks.delete(frame); callback?.(timestampMs); }, }; } describe("capped frame-loop runtime", () => { it("starts from Fiber's committed elapsed-seconds clock", () => { const queue = createFrameQueue(); const advances: number[] = []; const stop = startCappedFrameLoop({ targetFps: 30, getElapsedSeconds: () => 42, advance: (elapsedSeconds) => advances.push(elapsedSeconds), requestFrame: queue.requestFrame, cancelFrame: queue.cancelFrame, }); queue.run(1, 1_000); queue.run(2, 1_034); expect(advances).toEqual([42, 42.034]); stop(); }); it("does not schedule a zombie frame after re-entrant cleanup", () => { const queue = createFrameQueue(); let stop: () => void = () => undefined; stop = startCappedFrameLoop({ targetFps: 60, getElapsedSeconds: () => 0, advance: () => stop(), requestFrame: queue.requestFrame, cancelFrame: queue.cancelFrame, }); queue.run(1, 500); expect(queue.callbacks.size).toBe(0); expect(queue.cancelled).toEqual([1]); }); });