Files
feedBack/tests/js/highway_3d_pool_warm.test.js
T
byrongamatosandClaude Sonnet 4.6 4bb5d21a40 refactor(highway_3d): h3d-carve cut-16 — move initScene + bg-helpers + buildBoard to src/scene-init.js
VERBATIM-MOVE: initScene() (4215-5704), bg-helper functions (5706-6221), and
buildBoard() (6242-6571) extracted from screen.js into a new ES module
src/scene-init.js as createSceneInit({...~200 DI params}) → { initScene,
buildBoard, _bgUnmountStyle, _bcSyncMode }.

All factory-scope variables that other modules read go through getter+setter
pairs (no plain-value shorthands) to avoid the fork-class bug from cut-15.
Local alias pattern applied throughout: const _x = new T.Thing(); setX(_x);
closures use getX() to read current value.

Test retargets: highway_3d_context_loss, fret_spacing, pool_warm,
render_order, score_fx, sustain_bloom, sustain_rail, wide_fov — all updated
to search src/scene-init.js alongside screen.js, and patterns broadened for
DI forms (getRen().domElement, getFretG(), getFretTubeGeo(), etc.).

414/414 tests pass. ESLint: scene-init.js 0 errors, 1 max-lines warning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uZ169yfoFYArXz962g7KW
2026-09-06 06:29:27 +02:00

79 lines
3.9 KiB
JavaScript

// Source-level guards for the pool().warm() helper added in
// feedBack#226 — locks in:
// 1. The factory exposes .warm() (so future refactors don't quietly
// remove the boardInit pre-allocation strategy).
// 2. warm() coerces its argument via `cap | 0` + `Math.max(0, …)` so a
// non-finite or negative input can't spin a while-loop until OOM.
//
// Like the other highway_3d tests in this directory, this pattern-matches
// the source rather than executing it — the createHighway() closure owns
// canvas + WebGL lifecycle that's too heavy for a vm sandbox.
const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const SCREEN_JS = path.join(__dirname, '..', '..', 'plugins', 'highway_3d', 'screen.js');
const SCENE_INIT_JS = path.join(__dirname, '..', '..', 'plugins', 'highway_3d', 'src', 'scene-init.js'); // h3d-carve-16
// h3d-carve-6: pool() moved to src/materials.js; warm() call-sites remain in screen.js.
const MATERIALS_JS = path.join(__dirname, '..', '..', 'plugins', 'highway_3d', 'src', 'materials.js');
// Brace-balanced extraction so warm() / coercion checks scope to the
// `function pool(...)` body (matching the helper shape used in
// highway_note_state.test.js). Without scoping, a future helper named
// `warm(cap)` elsewhere in the file would satisfy these guards while
// the pool factory's contract was silently broken.
function extractBlock(src, signature) {
const start = src.indexOf(signature);
assert.ok(start !== -1, `signature '${signature}' not found`);
const openBrace = src.indexOf('{', start);
assert.ok(openBrace !== -1, `opening brace after '${signature}' not found`);
let depth = 1;
let i = openBrace + 1;
while (i < src.length && depth > 0) {
const ch = src[i];
if (ch === '{') depth++;
else if (ch === '}') depth--;
i++;
}
assert.ok(depth === 0, `unbalanced braces after '${signature}'`);
return src.slice(start, i);
}
test('pool factory exposes warm(cap)', () => {
// h3d-carve-6: pool() lives in src/materials.js (createMaterialBuilders).
const src = fs.readFileSync(MATERIALS_JS, 'utf8');
// The pool() factory's return object must include a `warm(cap)`
// method. Scope the match to the factory body so an unrelated
// future `warm(cap)` helper elsewhere in the file can't satisfy
// this guard.
const poolBody = extractBlock(src, 'function pool(parent, mk)');
assert.match(poolBody, /\bwarm\s*\(\s*cap\s*\)\s*\{/, 'pool factory must expose warm(cap)');
});
test('pool.warm coerces cap to a non-negative integer', () => {
// h3d-carve-6: pool() lives in src/materials.js (createMaterialBuilders).
const src = fs.readFileSync(MATERIALS_JS, 'utf8');
// Same scoping discipline as above — the coercion must live
// inside the pool factory's warm() body, not anywhere else.
const poolBody = extractBlock(src, 'function pool(parent, mk)');
assert.match(
poolBody,
/warm\s*\(\s*cap\s*\)\s*\{[\s\S]*?Math\.max\(\s*0\s*,\s*cap\s*\|\s*0\s*\)/,
'pool.warm must guard against non-finite / negative cap via Math.max(0, cap | 0)'
);
});
test('warm() is called at boardInit with renderer-scoped cap constants', () => {
const src = fs.readFileSync(SCREEN_JS, 'utf8') + '\n' + fs.readFileSync(SCENE_INIT_JS, 'utf8');
// The note / chord / lane / beat cap constants live inside the
// boardInit/initScene path (renderer-instance scope, not module
// scope); each must exist as a const and drive at least one .warm()
// call site.
for (const cap of ['_WARM_NOTE', '_WARM_CHORD', '_WARM_LANE', '_WARM_BEAT']) {
assert.match(src, new RegExp(`const ${cap}\\s*=`), `${cap} const must exist`);
assert.match(src, new RegExp(`\\.warm\\(\\s*${cap}\\b`), `${cap} must drive at least one .warm() call`);
}
});