test(algorithms): load simulations per test to fix flaky specs
Shared gpu-state.ts keeps the @babylonjs/core mock of the first spec that loads it. With isolate: false, other specs got that stale mock. Call vi.resetModules() and import the class in each test instead.
This commit is contained in:
1 parent
58b14a5b64
commit
05dc554d79
3 files changed
+35
-19
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
import {FluidParams, FluidSimulation} from './fluid-simulation';
|
import type {FluidParams} from './fluid-simulation';
|
||||||
import {DEFAULT_SETTINGS, MAX_PRESSURE_ITERATIONS, Splat} from './fluid.model';
|
import {DEFAULT_SETTINGS, MAX_PRESSURE_ITERATIONS, Splat} from './fluid.model';
|
||||||
|
|
||||||
const renderCalls: string[] = [];
|
const renderCalls: string[] = [];
|
||||||
@@ -62,7 +62,11 @@ function params(pressureIterations: number, splats: Splat[] = []): FluidParams {
|
|||||||
return {settings: {...DEFAULT_SETTINGS, pressureIterations}, splats};
|
return {settings: {...DEFAULT_SETTINGS, pressureIterations}, splats};
|
||||||
}
|
}
|
||||||
|
|
||||||
function start(readParams: () => FluidParams) {
|
// gpu-state.ts is shared between specs and keeps the @babylonjs/core of whichever spec loaded it first (the real
|
||||||
|
// one, or another spec's mock). A fresh module graph per test makes it use this spec's mock.
|
||||||
|
async function start(readParams: () => FluidParams) {
|
||||||
|
vi.resetModules();
|
||||||
|
const {FluidSimulation} = await import('./fluid-simulation');
|
||||||
renderCalls.length = 0;
|
renderCalls.length = 0;
|
||||||
disposedTargets = 0;
|
disposedTargets = 0;
|
||||||
disposedWrappers = 0;
|
disposedWrappers = 0;
|
||||||
@@ -78,9 +82,9 @@ function count(name: string): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('FluidSimulation', () => {
|
describe('FluidSimulation', () => {
|
||||||
it('drawsNothingUntilAllShadersReady', () => {
|
it('drawsNothingUntilAllShadersReady', async () => {
|
||||||
const readParams = vi.fn(() => params(1, [splat]));
|
const readParams = vi.fn(() => params(1, [splat]));
|
||||||
const {scene} = start(readParams);
|
const {scene} = await start(readParams);
|
||||||
ready['fluidAdvect'] = false;
|
ready['fluidAdvect'] = false;
|
||||||
|
|
||||||
scene.frame();
|
scene.frame();
|
||||||
@@ -93,9 +97,9 @@ describe('FluidSimulation', () => {
|
|||||||
expect(renderCalls).toContain('fluidSplat');
|
expect(renderCalls).toContain('fluidSplat');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('readsParamsAtFrameStart', () => {
|
it('readsParamsAtFrameStart', async () => {
|
||||||
let iterations = 5;
|
let iterations = 5;
|
||||||
const {scene} = start(() => params(iterations));
|
const {scene} = await start(() => params(iterations));
|
||||||
scene.frame();
|
scene.frame();
|
||||||
expect(count('fluidPressure')).toBe(5);
|
expect(count('fluidPressure')).toBe(5);
|
||||||
|
|
||||||
@@ -105,14 +109,14 @@ describe('FluidSimulation', () => {
|
|||||||
expect(count('fluidPressure')).toBe(12);
|
expect(count('fluidPressure')).toBe(12);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clampsPressureIterations', () => {
|
it('clampsPressureIterations', async () => {
|
||||||
const {scene} = start(() => params(1000));
|
const {scene} = await start(() => params(1000));
|
||||||
scene.frame();
|
scene.frame();
|
||||||
expect(count('fluidPressure')).toBe(MAX_PRESSURE_ITERATIONS);
|
expect(count('fluidPressure')).toBe(MAX_PRESSURE_ITERATIONS);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passOrder', () => {
|
it('passOrder', async () => {
|
||||||
const {scene} = start(() => params(2, [splat]));
|
const {scene} = await start(() => params(2, [splat]));
|
||||||
scene.frame();
|
scene.frame();
|
||||||
renderCalls.length = 0;
|
renderCalls.length = 0;
|
||||||
scene.frame();
|
scene.frame();
|
||||||
@@ -123,8 +127,8 @@ describe('FluidSimulation', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resetClearsBeforeSplats', () => {
|
it('resetClearsBeforeSplats', async () => {
|
||||||
const {scene, simulation} = start(() => params(1, [splat]));
|
const {scene, simulation} = await start(() => params(1, [splat]));
|
||||||
scene.frame();
|
scene.frame();
|
||||||
renderCalls.length = 0;
|
renderCalls.length = 0;
|
||||||
scene.frame();
|
scene.frame();
|
||||||
@@ -136,8 +140,8 @@ describe('FluidSimulation', () => {
|
|||||||
expect(renderCalls.slice(0, 4)).toEqual(['fluidClear', 'fluidClear', 'fluidClear', 'fluidSplat']);
|
expect(renderCalls.slice(0, 4)).toEqual(['fluidClear', 'fluidClear', 'fluidClear', 'fluidSplat']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('disposeFreesAllTargets', () => {
|
it('disposeFreesAllTargets', async () => {
|
||||||
const {simulation} = start(() => params(1));
|
const {simulation} = await start(() => params(1));
|
||||||
simulation.dispose();
|
simulation.dispose();
|
||||||
expect(disposedTargets).toBe(8);
|
expect(disposedTargets).toBe(8);
|
||||||
expect(disposedWrappers).toBe(8);
|
expect(disposedWrappers).toBe(8);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {GrayScottParams, GrayScottSimulation} from './gray-scott-simulation';
|
import type {GrayScottParams} from './gray-scott-simulation';
|
||||||
import {DEFAULT_SETTINGS} from './gray-scott.model';
|
import {DEFAULT_SETTINGS} from './gray-scott.model';
|
||||||
|
|
||||||
const renderCalls: string[] = [];
|
const renderCalls: string[] = [];
|
||||||
@@ -55,9 +55,13 @@ function paramsWithSteps(stepsPerFrame: number): GrayScottParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('GrayScottSimulation', () => {
|
describe('GrayScottSimulation', () => {
|
||||||
it('readsParamsAtFrameStart', () => {
|
it('readsParamsAtFrameStart', async () => {
|
||||||
let stepsPerFrame = 1;
|
let stepsPerFrame = 1;
|
||||||
const scene = fakeScene();
|
const scene = fakeScene();
|
||||||
|
// gpu-state.ts is shared between specs and keeps the @babylonjs/core of whichever spec loaded it first; a fresh
|
||||||
|
// module graph makes it use this spec's mock.
|
||||||
|
vi.resetModules();
|
||||||
|
const {GrayScottSimulation} = await import('./gray-scott-simulation');
|
||||||
const simulation = new GrayScottSimulation();
|
const simulation = new GrayScottSimulation();
|
||||||
simulation.init(scene as never, {} as never, 'float', () => paramsWithSteps(stepsPerFrame));
|
simulation.init(scene as never, {} as never, 'float', () => paramsWithSteps(stepsPerFrame));
|
||||||
scene.frame();
|
scene.frame();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {LeniaParams, LeniaSimulation} from './lenia-simulation';
|
import type {LeniaParams} from './lenia-simulation';
|
||||||
import {DEFAULT_SETTINGS} from './lenia.model';
|
import {DEFAULT_SETTINGS} from './lenia.model';
|
||||||
|
|
||||||
const renderCalls: {name: string; target: unknown}[] = [];
|
const renderCalls: {name: string; target: unknown}[] = [];
|
||||||
@@ -64,11 +64,15 @@ function paramsWithSteps(stepsPerFrame: number): LeniaParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('LeniaSimulation', () => {
|
describe('LeniaSimulation', () => {
|
||||||
it('keepsSeedWhileStepShaderCompiles', () => {
|
it('keepsSeedWhileStepShaderCompiles', async () => {
|
||||||
renderCalls.length = 0;
|
renderCalls.length = 0;
|
||||||
ready['leniaCopy'] = true;
|
ready['leniaCopy'] = true;
|
||||||
ready['leniaStep'] = false;
|
ready['leniaStep'] = false;
|
||||||
const scene = fakeScene();
|
const scene = fakeScene();
|
||||||
|
// gpu-state.ts is shared between specs and keeps the @babylonjs/core of whichever spec loaded it first; a fresh
|
||||||
|
// module graph makes it use this spec's mock.
|
||||||
|
vi.resetModules();
|
||||||
|
const {LeniaSimulation} = await import('./lenia-simulation');
|
||||||
const simulation = new LeniaSimulation();
|
const simulation = new LeniaSimulation();
|
||||||
simulation.init(scene as never, {} as never, 'float', () => paramsWithSteps(1));
|
simulation.init(scene as never, {} as never, 'float', () => paramsWithSteps(1));
|
||||||
simulation.seed(new Uint8Array(4));
|
simulation.seed(new Uint8Array(4));
|
||||||
@@ -85,12 +89,16 @@ describe('LeniaSimulation', () => {
|
|||||||
expect(firstStep.target).not.toBe(seededTarget);
|
expect(firstStep.target).not.toBe(seededTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('readsParamsAtFrameStart', () => {
|
it('readsParamsAtFrameStart', async () => {
|
||||||
renderCalls.length = 0;
|
renderCalls.length = 0;
|
||||||
ready['leniaCopy'] = true;
|
ready['leniaCopy'] = true;
|
||||||
ready['leniaStep'] = true;
|
ready['leniaStep'] = true;
|
||||||
let stepsPerFrame = 1;
|
let stepsPerFrame = 1;
|
||||||
const scene = fakeScene();
|
const scene = fakeScene();
|
||||||
|
// gpu-state.ts is shared between specs and keeps the @babylonjs/core of whichever spec loaded it first; a fresh
|
||||||
|
// module graph makes it use this spec's mock.
|
||||||
|
vi.resetModules();
|
||||||
|
const {LeniaSimulation} = await import('./lenia-simulation');
|
||||||
const simulation = new LeniaSimulation();
|
const simulation = new LeniaSimulation();
|
||||||
simulation.init(scene as never, {} as never, 'float', () => paramsWithSteps(stepsPerFrame));
|
simulation.init(scene as never, {} as never, 'float', () => paramsWithSteps(stepsPerFrame));
|
||||||
simulation.seed(new Uint8Array(4));
|
simulation.seed(new Uint8Array(4));
|
||||||
|
|||||||
Reference in new issue
Block a user