perf(led): the led-strips-v1 profile proves caches, disconnect and a late import (#780)

r1 M5: the runtime and field chunks release a card's frame and field cache
on disconnect (ledRelease from disconnectedCallback), never cache for a
disconnected card, and a chunk that lands after disconnect renders nothing
(the card's LED hook is connected-only). The profile now reports the three
caches of the shown space separately (shapes ≤ 50, visibility ≤ 50, retained
per-emitter fans ≤ 2500) through the runtime's ledStats, asserts 0 retained
entries and 0 live LED timers/frames/observers after every disconnect,
judges the Long Tasks of a 100-step camera series as well as the interaction
profile's camera scenario, runs one extra cold mount whose runtime response
is held while the card is removed, and enforces ≥ 7 samples after ≥ 1
warm-up — also on reports merged from parts (--warmup-only, --merge).

Issue: #780
User-Visible: no
This commit is contained in:
Codex
2026-10-02 20:54:50 +03:00
parent b40e8ff58d
commit e447364636
10 changed files with 375 additions and 97 deletions
+13
View File
@@ -158,3 +158,16 @@ test('AC2/r1 M2: an explicit valid room_id wins over the anchor room; a stale on
assert.equal(stripRoom(rooms, null, [250, 100], inA)?.id, 'A');
assert.equal(stripRoom(rooms, null, null, inA), undefined);
});
test('AC17/r1 M5: a released owner retains nothing; the stats count visibility entries and fans', async () => {
const { ledFieldCache, ledFieldStats, releaseLedField } = await import('../test-build/led-strip-field.js');
const owner = {};
const cache = ledFieldCache(owner);
cache.forSpace('a');
cache.read('k1', () => ({ pieces: [{ d: 'M0 0', clip: ['M0 0 Z', 'M1 1 Z'] }, { d: 'M1 1', clip: null }], box: { x: 0, y: 0, w: 1, h: 1 } }));
cache.read('k2', () => null);
assert.deepEqual(ledFieldStats(owner), { visibility: 2, sources: 2, recomputes: 2 });
releaseLedField(owner);
assert.deepEqual(ledFieldStats(owner), { visibility: 0, sources: 0, recomputes: 0 });
assert.notEqual(ledFieldCache(owner), cache, 'a new mount starts a new cache');
});
+4 -3
View File
@@ -305,8 +305,9 @@ test('#780 led-strips-v1: the derived fixture converts devices without adding ic
}
const budgets = JSON.parse(readFileSync(new URL('../demo/performance/budgets-led-strips.json', import.meta.url), 'utf8'));
assert.deepEqual(budgets.sizes['10x5'], { firstStableRenderMs: 3400, warmSpaceReadyMs: 1500, stateUpdateMs: 1000,
panZoomMs: 500, panZoomLongTaskMaxMs: 150, retainedHeapBytes: 64 * 1024 * 1024 });
panZoomMs: 500, panZoomLongTaskMaxMs: 150, retainedHeapBytes: 64 * 1024 * 1024, cameraSeriesLongTaskMaxMs: 150 });
assert.deepEqual(budgets.sizes['50x50'], { firstStableRenderMs: 5000, warmSpaceReadyMs: 1500, stateUpdateMs: 1500,
panZoomMs: 500, panZoomLongTaskMaxMs: 150, retainedHeapBytes: 64 * 1024 * 1024 });
assert.equal(budgets.cacheEntries, 50);
panZoomMs: 500, panZoomLongTaskMaxMs: 150, retainedHeapBytes: 64 * 1024 * 1024, cameraSeriesLongTaskMaxMs: 150 });
// ТЗ §13.2: the three caches of the shown space, judged separately (r1 M5).
assert.deepEqual(budgets.caches, { shapes: 50, visibility: 50, sources: 2500 });
});