diff --git a/src/app/features/scene/index.test.ts b/src/app/features/scene/index.test.ts index 5574d60c..c89fa3b0 100644 --- a/src/app/features/scene/index.test.ts +++ b/src/app/features/scene/index.test.ts @@ -11,6 +11,21 @@ const { createRidgeScene, createScene } = vi.hoisted(() => ({ })); vi.mock('./ridge/Ridge', () => ({ createRidgeScene })); vi.mock('./Scene', () => ({ createScene })); +// The physical sky is baked per call (tens of ms: atmosphere LUTs) and the clock here is fixed, so a state always +// draws the same sky. Baking once per state keeps the 21-offset sweep under the default timeout when the machine is +// loaded; the sky each build receives is still the one `ridgeSkyFor` makes for that state. +vi.mock('./skyForState', async (importOriginal) => { + const actual = await importOriginal(); + const baked = new Map>(); + return { + ...actual, + ridgeSkyFor: (...args: Parameters) => { + if (args.length !== 1) return actual.ridgeSkyFor(...args); + if (!baked.has(args[0])) baked.set(args[0], actual.ridgeSkyFor(args[0])); + return baked.get(args[0]) as ReturnType; + }, + }; +}); const canvas = {} as HTMLCanvasElement; const config = { tier: 'low' as const }; @@ -185,6 +200,19 @@ describe('startBackgroundScene follows the sky state', () => { expect(live.dispose).toHaveBeenCalledOnce(); }); + it('a rebuild for a sky that was replaced while its key was awaited builds nothing: only the newest sky is built', async () => { + createRidgeScene.mockReturnValueOnce(fakeScene()).mockReturnValueOnce(fakeScene()); + createRidgeScene.mockReturnValue(fakeScene()); + const scene = await startBackgroundScene(config, caps, { canvas }); + setSkyState('day'); + setSkyState('dusk'); + await flush(); + // The initial build and one rebuild, for dusk; day's rebuild stood down before building anything. + expect(createRidgeScene).toHaveBeenCalledTimes(2); + expect(hex(createRidgeScene.mock.calls[1][0].sky.top)).toBe(hex(ridgeSkyFor('dusk').top)); + scene?.dispose(); + }); + // A second sky change `ticks` microtasks after the first. True when it landed mid-build: the build for the first // change finished, was found stale and dropped (never shown), and only the newest sky was announced. const changeAfter = async (ticks: number) => {