79 lines
4.0 KiB
JavaScript
79 lines
4.0 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import ts from "../../web/node_modules/typescript/lib/typescript.js";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const sourcePath = path.join(root, "web/protocol/scene-ir.ts");
|
|
const source = ts.createSourceFile(sourcePath, fs.readFileSync(sourcePath, "utf8"), ts.ScriptTarget.Latest, true);
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-01/lighting-field-parity.json"), "utf8"));
|
|
|
|
function interfaceDeclaration(name) {
|
|
const declaration = source.statements.find((statement) => ts.isInterfaceDeclaration(statement) && statement.name.text === name);
|
|
assert.ok(declaration, `missing interface ${name}`);
|
|
return declaration;
|
|
}
|
|
|
|
function leafTypePaths(type, prefix) {
|
|
if (!ts.isTypeLiteralNode(type)) return [prefix];
|
|
return type.members.filter(ts.isPropertySignature).flatMap((member) =>
|
|
leafTypePaths(member.type, `${prefix}.${member.name.getText(source)}`));
|
|
}
|
|
|
|
function interfaceLeaves(name, include = () => true) {
|
|
return interfaceDeclaration(name).members.filter((member) =>
|
|
ts.isPropertySignature(member) && include(member.name.getText(source))).flatMap((member) =>
|
|
leafTypePaths(member.type, member.name.getText(source))).sort();
|
|
}
|
|
|
|
const expected = new Map([
|
|
["CAMERA", interfaceLeaves("CameraIR")],
|
|
["LIGHT", interfaceLeaves("LightIR")],
|
|
["WORLD", interfaceLeaves("WorldIR")],
|
|
["SCENE_COLOR_MANAGEMENT", interfaceLeaves("SceneIR", (name) => name === "renderEngine" || name === "colorManagement")],
|
|
]);
|
|
const stages = ["reader", "writer", "viewportMain", "viewportOffscreen"];
|
|
const stageStates = new Set(["VERIFIED", "PARTIAL", "METADATA_ONLY", "BLOCKED", "NOT_APPLICABLE"]);
|
|
const parityStates = new Set(["COMPLETE", "PARTIAL", "BLOCKED"]);
|
|
|
|
assert.equal(manifest.schemaVersion, 1);
|
|
assert.equal(manifest.task, "M11-01");
|
|
assert.equal(manifest.blenderVersion, "5.2.0");
|
|
assert.deepEqual(manifest.domains.map((domain) => domain.domain), [...expected.keys()]);
|
|
|
|
let total = 0;
|
|
const parityCounts = { COMPLETE: 0, PARTIAL: 0, BLOCKED: 0 };
|
|
for (const domain of manifest.domains) {
|
|
assert.deepEqual(domain.fields.map((field) => field.path).sort(), expected.get(domain.domain));
|
|
assert.equal(new Set(domain.fields.map((field) => field.path)).size, domain.fields.length);
|
|
assert.ok(Array.isArray(domain.evidence) && domain.evidence.length >= 4);
|
|
for (const evidence of domain.evidence) assert.ok(fs.existsSync(path.join(root, evidence)), `${domain.domain} evidence is missing: ${evidence}`);
|
|
for (const field of domain.fields) {
|
|
for (const stage of stages) assert.ok(stageStates.has(field[stage]), `${domain.domain}.${field.path}.${stage} is invalid`);
|
|
assert.ok(parityStates.has(field.parity), `${domain.domain}.${field.path}.parity is invalid`);
|
|
if (field.parity === "COMPLETE") {
|
|
assert.ok(stages.every((stage) => field[stage] === "VERIFIED" || field[stage] === "NOT_APPLICABLE"), `${domain.domain}.${field.path} overclaims COMPLETE`);
|
|
}
|
|
parityCounts[field.parity] += 1;
|
|
total += 1;
|
|
}
|
|
}
|
|
|
|
const requiredBlocks = [
|
|
["CAMERA", "orthoScale", "viewportMain"],
|
|
["CAMERA", "depthOfField.enabled", "viewportMain"],
|
|
["LIGHT", "areaSpread", "viewportMain"],
|
|
["LIGHT", "sunAngle", "viewportOffscreen"],
|
|
["WORLD", "environmentRotation", "reader"],
|
|
["SCENE_COLOR_MANAGEMENT", "colorManagement.displayDevice", "viewportMain"],
|
|
["SCENE_COLOR_MANAGEMENT", "colorManagement.gamma", "viewportOffscreen"],
|
|
];
|
|
for (const [domainName, fieldPath, stage] of requiredBlocks) {
|
|
const field = manifest.domains.find((domain) => domain.domain === domainName)?.fields.find((candidate) => candidate.path === fieldPath);
|
|
assert.equal(field?.[stage], "BLOCKED", `${domainName}.${fieldPath}.${stage} must remain BLOCKED`);
|
|
}
|
|
|
|
assert.equal(total, 60);
|
|
process.stdout.write(`lighting-field-parity-ok fields=${total} complete=${parityCounts.COMPLETE} partial=${parityCounts.PARTIAL} blocked=${parityCounts.BLOCKED}\n`);
|