import { describe, expect, it } from "vitest"; import { createFrameRateGovernor, resetFrameRateGovernor, stepFrameRateGovernor, } from "./frameRateGovernor"; function renderCount(targetFps: 30 | 60, sourceFps: number, durationMs = 1_000): number { let state = createFrameRateGovernor(targetFps); let renders = 0; for (let timestamp = 0; timestamp <= durationMs; timestamp += 1_000 / sourceFps) { const step = stepFrameRateGovernor(state, timestamp); state = step.state; if (step.shouldRender) renders += 1; } return renders; } describe("frame-rate governor", () => { it("holds capped modes near their target on high-refresh displays", () => { expect(renderCount(30, 144)).toBeGreaterThanOrEqual(30); expect(renderCount(30, 144)).toBeLessThanOrEqual(31); expect(renderCount(60, 240)).toBeGreaterThanOrEqual(60); expect(renderCount(60, 240)).toBeLessThanOrEqual(61); }); it("renders every source callback in display mode", () => { let state = createFrameRateGovernor("display", 4); for (const timestamp of [1_000, 1_008, 1_016]) { const step = stepFrameRateGovernor(state, timestamp); state = step.state; expect(step.shouldRender).toBe(true); } expect(state.elapsedSeconds).toBeCloseTo(4.016); }); it("skips missed deadlines without emitting a catch-up burst", () => { let state = createFrameRateGovernor(60); state = stepFrameRateGovernor(state, 0).state; const delayed = stepFrameRateGovernor(state, 100); expect(delayed.shouldRender).toBe(true); expect(delayed.state.nextDeadlineMs).toBeGreaterThan(100); const sameTimestamp = stepFrameRateGovernor(delayed.state, 100); expect(sameTimestamp.shouldRender).toBe(false); }); it("preserves elapsed time while resetting the wall-clock baseline", () => { let state = createFrameRateGovernor(30, 2); state = stepFrameRateGovernor(state, 1_000).state; state = stepFrameRateGovernor(state, 1_034).state; const reset = resetFrameRateGovernor(state, 60); const resumed = stepFrameRateGovernor(reset, 90_000); expect(resumed.shouldRender).toBe(true); expect(resumed.elapsedSeconds).toBeCloseTo(state.elapsedSeconds); }); it("never moves elapsed time backwards", () => { let state = createFrameRateGovernor(60); state = stepFrameRateGovernor(state, 20).state; state = stepFrameRateGovernor(state, 40).state; const backwards = stepFrameRateGovernor(state, 10); expect(backwards.elapsedSeconds).toBe(state.elapsedSeconds); }); });