import assert from 'node:assert/strict' import { createRegionLoadingController } from '../../src/modules/cockpit/services/regionLoading.js' // Replay the load boundary seen in the low-spec exports: coarse is ready, // close candidates remain visible, but the camera never stops moving. let clock = 0, pose = 'initial', visible = false const started = [], pending = new Map() const controller = createRegionLoadingController({ regions: [1, 2].map(id => ({ id, contentCount: 1, decodedGeometryBytes: 1024 })), load: region => { started.push(region.id); return new Promise(resolve => pending.set(region.id, resolve)) }, add() {}, remove() {}, requestRender() {}, distance: region => visible ? region.id * 10 : Infinity, canStart: () => true, readCameraKey: () => pose, now: () => clock, setTimer: () => 1, clearTimer() {}, }) try { controller.refresh() visible = true for (let i = 1; i <= 8; i++) { clock = i * 180 pose = 'moving-' + i controller.onRender() } assert.equal(controller.frontierSnapshot().moving, true) const whileMoving = started.slice(), movingCount = controller.snapshot().length clock += 1000 controller.refresh() console.log(JSON.stringify({ whileMoving, afterSettling: started })) assert.deepEqual(started, [1, 2], 'Same candidates start under the unchanged stationary concurrency limit') assert.deepEqual(whileMoving, [1], 'Continuous movement must admit one nearest region, not starve all fine loading') assert.equal(movingCount, 1, 'Movement keeps at most one initial load in flight') console.log('PASS moving admission: visible near candidate progresses through one bounded load slot') } finally { controller.detach() for (const resolve of pending.values()) resolve({ show: false, tilesLoaded: false }) await Promise.resolve() } // Boundary cases use the same public controller and explicit asynchronous loads. const flush = async () => { await Promise.resolve(); await Promise.resolve() } async function check(options, scenario) { const { count = 3, ...controllerOptions } = options let clock = 0, pose = 0 const state = { visible: false, coarseReady: true } const started = [], added = [], removed = [], models = new Map(), waits = new Map() const controller = createRegionLoadingController({ regions: Array.from({ length: count }, (_, i) => ({ id: i + 1, contentCount: 1, decodedGeometryBytes: 1024 })), distance: r => state.visible ? r.id * 10 : Infinity, readCameraKey: () => String(pose), now: () => clock, canStart: () => state.coarseReady, coverageReady: e => e.instance.covered === true, load: r => { started.push(r.id); return new Promise((resolve, reject) => waits.set(r.id, { resolve, reject })) }, add: m => added.push(m), remove: m => removed.push(m), requestRender() {}, setTimer: () => 1, clearTimer() {}, ...controllerOptions, }) controller.refresh() const h = { controller, state, started, added, removed, async step(ms = 180, moving = true) { clock += ms; if (moving) pose++; controller.onRender(); await flush() }, async resolveLoad(id, ready = false) { const wait = waits.get(id) assert.ok(wait, 'Only pending loads can resolve') const m = { show: false, tilesLoaded: ready, covered: ready } models.set(id, m); wait.resolve(m); waits.delete(id); await flush(); return m }, markReady(id) { const m = models.get(id); m.tilesLoaded = m.covered = true }, async fail(id) { waits.get(id).reject(new Error('fixture load failed')); waits.delete(id); await flush() }, } try { await scenario(h) } finally { controller.detach() for (const wait of waits.values()) wait.resolve({ show: false, tilesLoaded: false, covered: false }) await flush() } } await check({}, async h => { h.state.visible = true await h.step() assert.deepEqual(h.started, [1], 'Nearest candidate starts first during movement') await h.resolveLoad(1) // fromUrl resolved, but content is not ready yet. await h.step() assert.deepEqual(h.started, [1], 'Streaming keeps the single moving slot occupied') assert.equal(h.controller.detailReady(1), false, 'Incomplete coverage keeps coarse fallback') h.markReady(1) await h.step() assert.deepEqual(h.started, [1, 2], 'Arrival releases the slot without waiting for camera settle') }) await check({}, async h => { h.state.visible = true await h.step(300, false) assert.deepEqual(h.started, [1, 2], 'Stationary phase retains configured parallelism') await h.step() assert.equal(h.removed.length, 0, 'Moving does not cancel the two existing initial loads') await h.resolveLoad(1, true); await h.step() assert.deepEqual(h.started, [1, 2], 'Do not add a new load while one existing slot remains') await h.resolveLoad(2, true); await h.step() assert.deepEqual(h.started, [1, 2, 3], 'Resume bounded moving admission after existing work completes') }) for (const budget of [{ maxContents: 1 }, { maxDecodedBytes: 1024 }]) { await check({ budget }, async h => { h.state.visible = true await h.step(); await h.resolveLoad(1, true); await h.step() await h.step(1000, false) assert.deepEqual(h.started, [1], 'Neither movement nor settling bypasses the selection budget') }) } await check({ maxConcurrentLoads: 1 }, async h => { h.state.visible = true await h.step(300, false) assert.deepEqual(h.started, [1], 'Explicit single-slot configuration still applies when stationary') await h.resolveLoad(1, true); await h.step(300, false) assert.deepEqual(h.started, [1, 2]) }) await check({}, async h => { h.state.visible = true; h.state.coarseReady = false await h.step(); await h.step() assert.deepEqual(h.started, [], 'Coarse readiness gate remains mandatory') h.state.coarseReady = true await h.step(); assert.deepEqual(h.started, [1]) h.controller.setPipeline(true) const m = await h.resolveLoad(1, true) await h.step() assert.equal(h.added.length, 0, 'Late completion cannot attach in pipeline mode') assert.deepEqual(h.removed, [m], 'Rejected late model is released exactly once') assert.deepEqual(h.started, [1], 'Pipeline mode does not admit new ground regions') }) await check({ count: 1 }, async h => { h.state.visible = true; await h.step(); await h.fail(1) await h.step(); assert.deepEqual(h.started, [1], 'Failure cooldown prevents repeated starts') await h.step(15001) assert.deepEqual(h.started, [1, 1], 'Retry can progress during movement after cooldown') }) await check({ count: 1 }, async h => { h.state.visible = true; await h.step() h.state.visible = false; await h.step() const m = await h.resolveLoad(1, true) assert.equal(h.added.length, 0, 'Withdrawn candidate cannot attach after async completion') assert.deepEqual(h.removed, [m]) h.state.visible = true; await h.step() assert.deepEqual(h.started, [1, 1], 'Rejected completion releases its slot for a new candidate') }) console.log('PASS moving admission boundaries: progress, streaming cap, static transition, budgets, coarse gate, cancellation, retry')