Fourteen visual challenge fixtures and the first visuals for reference exhibits A through D, all executing without a line of subject-specific renderer code — which is the claim slice 4g exists to test. A subject the engine needs special handling for is a subject the format cannot express. The standalone CLI validator no longer disagrees with the production one. It had been falling through to ERR_UNSUPPORTED_TARGET for every `visuals.*` binding target, so legal exhibits — including the committed minimal-visual fixture — failed the CLI while passing the runtime. The four section 8.1 visual target families are now recognized in both, and a test runs the CLI validator across every exhibit and challenge fixture so the two paths cannot drift apart again. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01ShxxFqFmCUDQnQvFNm4TKy
56 lines
3.0 KiB
JavaScript
56 lines
3.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { visualChallenges, visualExhibits } from '../tools/visual-challenge-fixtures.mjs';
|
|
import { validateExhibit } from '../src/runtime/validator.js';
|
|
import { VisualEngine } from '../src/runtime/visual-engine.js';
|
|
import { ResolutionEngine } from '../src/runtime/resolution.js';
|
|
import { SeededRNG } from '../src/runtime/rng.js';
|
|
import { Script } from 'node:vm';
|
|
import { readFileSync } from 'node:fs';
|
|
import { buildVisualAcceptance } from '../tools/build-visual-acceptance.mjs';
|
|
|
|
for (const fixture of [...visualChallenges, ...visualExhibits]) test(`4g ${fixture.id}: validates, executes, draws, and reproduces across frame rates`, () => {
|
|
assert.deepEqual(validateExhibit(fixture.document).errors, []);
|
|
const run = cadence => {
|
|
const rng = new SeededRNG(fixture.document.runtime.seed);
|
|
const resolution = new ResolutionEngine(fixture.document, rng);
|
|
const engine = new VisualEngine(fixture.document, { rng, resolution });
|
|
if (fixture.id === 'transient-impact') engine.spawn('burst');
|
|
let plan;
|
|
for (let i = 0; i < 120; i++) {
|
|
resolution.signals.set('signals.audio.energy', (Math.sin(i / 20) + 1) / 2);
|
|
resolution.signals.set('signals.audio.low', (Math.cos(i / 30) + 1) / 2);
|
|
resolution.advance(1000 / 60); engine.advance(1000 / 60);
|
|
if (i % cadence === 0 || i === 119) plan = engine.planFrame({ width: 960, height: 540 });
|
|
}
|
|
assert.equal(plan.deferred.length, 0);
|
|
assert.ok(plan.layers.some(layer => layer.nodes.length > 0));
|
|
const finite = object => {
|
|
if (typeof object === 'number') assert.ok(Number.isFinite(object));
|
|
else if (object && typeof object === 'object') for (const value of Object.values(object)) finite(value);
|
|
};
|
|
finite(plan.layers);
|
|
engine.dispose(); assert.equal(engine.instances.size, 0); assert.equal(resolution.automation.size, 0);
|
|
return plan.layers;
|
|
};
|
|
assert.deepEqual(run(1), run(4));
|
|
});
|
|
|
|
test('4g standalone acceptance build is deterministic, contains every fixture and parses as JavaScript', () => {
|
|
const first = buildVisualAcceptance(), second = buildVisualAcceptance();
|
|
assert.equal(first.html, second.html); assert.equal(first.count, 18);
|
|
const script = /<script>([\s\S]*)<\/script>/.exec(first.html)[1];
|
|
assert.doesNotThrow(() => new Script(script));
|
|
assert.ok(!/<script[^>]+src=|<link[^>]+href=/.test(first.html));
|
|
for (const fixture of visualChallenges) assert.deepEqual(JSON.parse(readFileSync(new URL(`../exhibits/visual-challenges/${fixture.id}.xzbt`, import.meta.url))), fixture.document);
|
|
});
|
|
|
|
test('C6: standalone CLI validator accepts all visual challenges and exhibits', async () => {
|
|
const { ExhibitValidator } = await import('../tools/validate-exhibit.mjs');
|
|
for (const fixture of [...visualChallenges, ...visualExhibits]) {
|
|
const validator = new ExhibitValidator(fixture.document, `${fixture.id}.xzbt`);
|
|
const result = validator.validate();
|
|
assert.deepEqual(result.errors, [], `CLI validator errors on ${fixture.id}: ${JSON.stringify(result.errors)}`);
|
|
}
|
|
});
|