import { vec2 } from 'gl-matrix'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; vi.hoisted(() => { Object.defineProperty(globalThis, 'localStorage', { configurable: true, value: { getItem: vi.fn(() => null), setItem: vi.fn(), }, }); }); import { appConfig } from '../config'; import { AgentGenerationPipeline } from '../pipelines/agents/agent-generation/agent-generation-pipeline'; import { settings } from '../settings'; import { AgentPopulation } from './agent-population'; const originalAgentBudgetMax = settings.agentBudgetMax; const originalBrushSize = settings.brushSize; const originalSelectedColorIndex = settings.selectedColorIndex; const originalSpawnPerPixel = settings.spawnPerPixel; const createPopulation = () => { const pipeline = { maxAgentCount: 10_000_000, writeAgents: vi.fn(), resizeAgents: vi.fn(), compactAgents: vi.fn(), } as unknown as AgentGenerationPipeline; return new AgentPopulation(pipeline); }; const setPopulationActiveCount = (population: AgentPopulation, activeCount: number) => { Object.assign(population as unknown as Record, { activeCount, }); }; describe('AgentPopulation adaptive budget', () => { beforeEach(() => { settings.agentBudgetMax = 1_000_000; settings.brushSize = 1; settings.selectedColorIndex = 0; settings.spawnPerPixel = 1; }); afterEach(() => { settings.agentBudgetMax = originalAgentBudgetMax; settings.brushSize = originalBrushSize; settings.selectedColorIndex = originalSelectedColorIndex; settings.spawnPerPixel = originalSpawnPerPixel; }); it('expands beyond the 1M start cap only when new agents arrive under healthy FPS', () => { const population = createPopulation(); setPopulationActiveCount(population, 1_000_000); population.growBudget(1 / 60, 60, 60); population.spawnStrokeAgents(vec2.fromValues(0, 0), vec2.fromValues(16, 0)); expect(settings.agentBudgetMax).toBeGreaterThan(1_000_000); expect(population.activeAgentCount).toBeGreaterThan(1_000_000); expect(settings.agentBudgetMax).toBeLessThanOrEqual( appConfig.simulation.globalAgentCap ); }); it('decreases the cap and active count slowly when FPS falls below the threshold', () => { const population = createPopulation(); setPopulationActiveCount(population, 1_000_000); population.growBudget(10, 50, 60); expect(settings.agentBudgetMax).toBe(appConfig.simulation.budget.adaptiveCapMin); expect(population.activeAgentCount).toBe( appConfig.simulation.budget.adaptiveCapMin ); }); });