scene: two quick sky changes build the last sky; Night drift test is exact #57

Merged
void merged 1 commit from void/scene-test-strength into dev 2026-11-10 01:55:22 +00:00
3 changed files with 20 additions and 5 deletions

View file

@ -4,6 +4,7 @@ import { baked } from '../../../sky/baked';
import type { Scene } from './Scene';
import { startBackgroundScene } from './index';
import { setSkyClock, setSkyState } from './skyState';
import { ridgeSkyFor } from './skyForState';
const { createRidgeScene, createScene } = vi.hoisted(() => ({
createRidgeScene: vi.fn(),
@ -155,16 +156,28 @@ describe('startBackgroundScene follows the sky state', () => {
expect(createRidgeScene).toHaveBeenCalledOnce();
});
it('keeps only the latest of two quick changes', async () => {
it('keeps only the latest of two quick changes: the last sky is built, the others are not left running', async () => {
const scenes = [fakeScene(), fakeScene(), fakeScene()];
scenes.forEach((s) => createRidgeScene.mockReturnValueOnce(s));
const scene = await startBackgroundScene(config, caps, { canvas });
setSkyState('day');
setSkyState('night');
setSkyState('dusk');
await flush();
expect(scenes[1].dispose).toHaveBeenCalledOnce();
expect(scenes[2].dispose).not.toHaveBeenCalled();
// The sky last built is dusk's, and it is the live scene: a login reaches it and nothing else.
const built = createRidgeScene.mock.calls.length;
expect(hex(createRidgeScene.mock.calls[built - 1][0].sky.top)).toBe(
hex(ridgeSkyFor('dusk').top)
);
const live = scenes[built - 1];
scene?.login();
expect(live.login).toHaveBeenCalledOnce();
scenes.filter((s) => s !== live).forEach((s) => expect(s.login).not.toHaveBeenCalled());
// Every scene but the live one was disposed, the live one not yet.
scenes.slice(0, built).forEach((s) => {
expect(s.dispose).toHaveBeenCalledTimes(s === live ? 0 : 1);
});
scene?.dispose();
expect(live.dispose).toHaveBeenCalledOnce();
});
it('leaves the ray-march scene alone: it has one fixed dusk', async () => {

View file

@ -95,8 +95,10 @@ export async function startBackgroundScene(
let generation = 0;
// A rebuild is for a different sky ('auto' crossing a boundary, a new choice), not for a repeat of the one drawn.
const rebuild = async () => {
// The choice can move while the key is awaited; that change has its own rebuild, so this one stands down.
const next = getSkyState();
const key = await skyKey(config, next);
if (getSkyState() !== next) return;
if ((key !== null && key === built) || disposed) return;
generation += 1;
const mine = generation;

View file

@ -92,7 +92,7 @@ describe('ink of the theme a state picks, on bare sky', () => {
});
it('keeps the lightest Night sky the link was fitted to equal to the Night backdrop (no drift)', () => {
expect(Math.abs(NIGHT_SKY_LIGHTEST - skyBackdrop('night').lightest)).toBeLessThan(1e-6);
expect(NIGHT_SKY_LIGHTEST).toBe(skyBackdrop('night').lightest);
});
it.each([