Files
healer-man/src/scene/cappedFrameLoopRuntime.test.ts
T
2026-08-30 14:16:56 -04:00

66 lines
1.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { startCappedFrameLoop } from "./cappedFrameLoopRuntime";
function createFrameQueue() {
const callbacks = new Map<number, (timestampMs: number) => 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]);
});
});