163 lines
4.3 KiB
TypeScript
163 lines
4.3 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import { createEmptySceneDocument } from "../../src/document/scene-document";
|
|
import {
|
|
EditorSimulationController,
|
|
type EditorSimulationUiSnapshot
|
|
} from "../../src/runtime-three/editor-simulation-controller";
|
|
import { buildRuntimeSceneFromDocument } from "../../src/runtime-three/runtime-scene-build";
|
|
|
|
function createManualFrameController(options: {
|
|
uiSnapshotIntervalSeconds?: number;
|
|
onBuild?: () => void;
|
|
} = {}) {
|
|
return new EditorSimulationController({
|
|
uiSnapshotIntervalSeconds: options.uiSnapshotIntervalSeconds,
|
|
requestAnimationFrame: () => 1,
|
|
cancelAnimationFrame: () => undefined,
|
|
buildRuntimeScene: (document, buildOptions) => {
|
|
options.onBuild?.();
|
|
return buildRuntimeSceneFromDocument(document, buildOptions);
|
|
}
|
|
});
|
|
}
|
|
|
|
describe("EditorSimulationController", () => {
|
|
it("advances the editor clock only while playing", () => {
|
|
const document = createEmptySceneDocument();
|
|
document.time.dayLengthMinutes = 24;
|
|
document.time.startTimeOfDayHours = 6;
|
|
const controller = createManualFrameController();
|
|
controller.updateInputs({
|
|
document,
|
|
loadedModelAssets: {}
|
|
});
|
|
|
|
const initialClock = controller.getUiSnapshot().clock;
|
|
expect(initialClock?.timeOfDayHours).toBe(6);
|
|
|
|
controller.play();
|
|
controller.advance(60);
|
|
|
|
const playingClock = controller.getUiSnapshot().clock;
|
|
expect(playingClock?.timeOfDayHours).toBeGreaterThan(6);
|
|
|
|
controller.pause();
|
|
controller.advance(60);
|
|
|
|
expect(controller.getUiSnapshot().clock).toEqual(playingClock);
|
|
});
|
|
|
|
it("publishes coarse UI snapshots at a bounded tick rate", () => {
|
|
const document = createEmptySceneDocument();
|
|
const controller = createManualFrameController({
|
|
uiSnapshotIntervalSeconds: 0.5
|
|
});
|
|
const snapshots: EditorSimulationUiSnapshot[] = [];
|
|
|
|
controller.updateInputs({
|
|
document,
|
|
loadedModelAssets: {}
|
|
});
|
|
controller.subscribeUiSnapshot((snapshot) => {
|
|
snapshots.push(snapshot);
|
|
});
|
|
snapshots.length = 0;
|
|
|
|
controller.play();
|
|
expect(snapshots).toHaveLength(1);
|
|
snapshots.length = 0;
|
|
|
|
controller.advance(0.2);
|
|
controller.advance(0.2);
|
|
expect(snapshots).toHaveLength(0);
|
|
|
|
controller.advance(0.1);
|
|
expect(snapshots).toHaveLength(1);
|
|
});
|
|
|
|
it("rebuilds the cached base simulation scene only when inputs change", () => {
|
|
let buildCount = 0;
|
|
const document = createEmptySceneDocument();
|
|
const loadedModelAssets = {};
|
|
const controller = createManualFrameController({
|
|
onBuild: () => {
|
|
buildCount += 1;
|
|
}
|
|
});
|
|
|
|
controller.updateInputs({
|
|
document,
|
|
loadedModelAssets
|
|
});
|
|
expect(buildCount).toBe(1);
|
|
|
|
controller.play();
|
|
controller.advance(0.1);
|
|
controller.advance(0.1);
|
|
expect(buildCount).toBe(1);
|
|
|
|
controller.updateInputs({
|
|
document,
|
|
loadedModelAssets
|
|
});
|
|
expect(buildCount).toBe(1);
|
|
|
|
const renamedDocument = {
|
|
...document,
|
|
name: "Rebuilt Scene"
|
|
};
|
|
controller.updateInputs({
|
|
document: renamedDocument,
|
|
loadedModelAssets
|
|
});
|
|
expect(buildCount).toBe(2);
|
|
|
|
const retimedDocument = {
|
|
...renamedDocument,
|
|
time: {
|
|
...renamedDocument.time,
|
|
dayLengthMinutes: 12
|
|
}
|
|
};
|
|
controller.updateInputs({
|
|
document: retimedDocument,
|
|
loadedModelAssets
|
|
});
|
|
expect(buildCount).toBe(3);
|
|
|
|
controller.updateInputs({
|
|
document: retimedDocument,
|
|
loadedModelAssets: {}
|
|
});
|
|
expect(buildCount).toBe(4);
|
|
});
|
|
|
|
it("emits frame updates without publishing a UI snapshot every frame", () => {
|
|
const document = createEmptySceneDocument();
|
|
const controller = createManualFrameController({
|
|
uiSnapshotIntervalSeconds: 1
|
|
});
|
|
const frameListener = vi.fn();
|
|
const uiListener = vi.fn();
|
|
|
|
controller.updateInputs({
|
|
document,
|
|
loadedModelAssets: {}
|
|
});
|
|
controller.subscribeFrame(frameListener);
|
|
controller.subscribeUiSnapshot(uiListener);
|
|
frameListener.mockClear();
|
|
uiListener.mockClear();
|
|
|
|
controller.play();
|
|
frameListener.mockClear();
|
|
uiListener.mockClear();
|
|
controller.advance(0.25);
|
|
controller.advance(0.25);
|
|
|
|
expect(frameListener).toHaveBeenCalledTimes(2);
|
|
expect(uiListener).not.toHaveBeenCalled();
|
|
});
|
|
});
|