107 lines
10 KiB
JavaScript
107 lines
10 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { execFileSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const write = process.argv.includes("--write");
|
|
const absolute = (file) => path.join(root, file);
|
|
const read = (file) => fs.readFileSync(absolute(file), "utf8");
|
|
const readJson = (file) => JSON.parse(read(file));
|
|
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(absolute(file))).digest("hex");
|
|
const outputRoot = "tests/golden/WBV2-P12-003";
|
|
const productionPath = `${outputRoot}/material-animation-production.json`;
|
|
const secondProductionPath = `${outputRoot}/material-animation-production.second.json`;
|
|
const reportPath = `${outputRoot}/material-animation-receipt-report.json`;
|
|
const statusPath = "docs/status/WBV2-P12-003.md";
|
|
const planPath = `${outputRoot}/domain-expansion-task-plan.json`;
|
|
const manifestPath = `${outputRoot}/manifest.json`;
|
|
const parentPath = "tests/golden/WBV2-P12-002/manifest.json";
|
|
const materialManifestPath = "tests/golden/WBV2-P3-001/manifest.json";
|
|
const materialReportPath = "tests/golden/WBV2-P3-001/material-report.json";
|
|
const animationManifestPath = "tests/golden/WBV2-P3-002/manifest.json";
|
|
const animationReportPath = "tests/golden/WBV2-P3-002/animation-report.json";
|
|
|
|
const parent = readJson(parentPath);
|
|
assert.equal(parent.task, "WBV2-P12-002");
|
|
assert.equal(parent.status, "done");
|
|
assert.equal(parent.nextTask, "WBV2-P12-003");
|
|
for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path);
|
|
for (const [manifestPathValue, reportPathValue] of [[materialManifestPath, materialReportPath], [animationManifestPath, animationReportPath]]) {
|
|
const manifest = readJson(manifestPathValue);
|
|
const report = readJson(reportPathValue);
|
|
assert.equal(manifest.status, "done");
|
|
assert.equal(report.desktop.status, "EXACT");
|
|
assert.equal(report.wasm.status, "EXACT");
|
|
assert.equal(report.saveReopen ?? report.wasm.saveReopen, "EXACT");
|
|
const bound = Object.values(manifest.artifacts ?? {}).find((artifact) => artifact.path === reportPathValue);
|
|
assert.ok(bound);
|
|
assert.equal(bound.sha256, sha256(reportPathValue));
|
|
}
|
|
|
|
assert.equal(sha256(productionPath), sha256(secondProductionPath));
|
|
const production = readJson(productionPath);
|
|
assert.equal(production.task, "WBV2-P12-003");
|
|
assert.equal(production.browser, "chromium");
|
|
assert.equal(production.queueMutation, false);
|
|
assert.equal(production.material.sourceUnsupportedNode, true);
|
|
assert.equal(production.material.finalRevision, production.material.baseRevision + 4);
|
|
assert.equal(production.material.activeUVMap, "WBV2P3001SecondaryUV");
|
|
assert.equal(production.material.invalidUV.code, "INVALID_ARGUMENT");
|
|
assert.equal(production.material.revisionAfterFailure, production.material.finalRevision);
|
|
assert.equal(production.material.saveReopenExact, true);
|
|
assert.equal(production.animation.insertedRevision, production.animation.baseRevision + 1);
|
|
assert.equal(production.animation.finalRevision, production.animation.baseRevision + 2);
|
|
assert.deepEqual(production.animation.locationFrames, [1, 10, 20, 25]);
|
|
assert.equal(production.animation.interpolation, "LINEAR");
|
|
assert.equal(production.animation.staleFrame.code, "INVALID_ARGUMENT");
|
|
assert.equal(production.animation.revisionAfterFailure, production.animation.finalRevision);
|
|
assert.equal(production.animation.saveReopenExact, true);
|
|
assert.ok(production.material.savedByteLength > 0);
|
|
assert.ok(production.animation.savedByteLength > 0);
|
|
|
|
const receipt = {
|
|
schemaVersion: 1,
|
|
task: "WBV2-P12-003",
|
|
operation: "MATERIAL_ANIMATION_ENGINE_RECEIPT",
|
|
status: "PASS",
|
|
browser: "chromium",
|
|
queueMutation: false,
|
|
source: { material: { task: "WBV2-P3-001", manifest: materialManifestPath, manifestSha256: sha256(materialManifestPath), report: materialReportPath, reportSha256: sha256(materialReportPath) }, animation: { task: "WBV2-P3-002", manifest: animationManifestPath, manifestSha256: sha256(animationManifestPath), report: animationReportPath, reportSha256: sha256(animationReportPath) }, reusedReadOnly: true },
|
|
production: { report: productionPath, reportSha256: sha256(productionPath), secondReport: secondProductionPath, secondReportSha256: sha256(secondProductionPath), deterministic: true, material: { materialId: production.material.materialId, meshId: production.material.meshId, revisions: [production.material.baseRevision, production.material.finalRevision], activeUVMap: production.material.activeUVMap, baseColor: production.material.baseColor, roughness: production.material.roughness, metallic: production.material.metallic, unsupportedNodeRetained: production.material.sourceUnsupportedNode, invalidUVRejected: true, savedByteLength: production.material.savedByteLength, saveReopen: "EXACT" }, animation: { animationId: production.animation.animationId, revisions: [production.animation.baseRevision, production.animation.insertedRevision, production.animation.finalRevision], channelCount: production.animation.channelCount, locationFrames: production.animation.locationFrames, interpolation: production.animation.interpolation, staleFrameRejected: true, savedByteLength: production.animation.savedByteLength, saveReopen: "EXACT" } },
|
|
assertions: { materialMainChanged: true, animationMainChanged: true, sourceHashesBound: true, unsupportedGraphExplicit: true, staleFrameRejected: true, saveReopenExact: true, historicalReproduction: false },
|
|
nextTask: "WBV2-P12-004",
|
|
};
|
|
if (!write) { process.stdout.write("wbv2-material-animation-receipt-ok task=WBV2-P12-003 material=EXACT animation=EXACT saveReopen=EXACT deterministic=true next=WBV2-P12-004\n"); process.exit(0); }
|
|
fs.mkdirSync(absolute(outputRoot), { recursive: true });
|
|
fs.writeFileSync(absolute(reportPath), `${JSON.stringify(receipt, null, 2)}\n`);
|
|
fs.writeFileSync(absolute(statusPath), `# WBV2-P12-003 status\n\n- task: \`WBV2-P12-003\`; parent: \`WBV2-P12-002\`; status: \`done\`\n- material/UV: \`4\` Main mutations; Principled values and UV map persisted\n- animation/TRS: keyframe frame \`25\` + interpolation \`LINEAR\`; persisted\n- save/reopen: \`EXACT\`; invalid UV and stale frame rejected\n- existing P3 source receipts reused read-only; no duplicate historical run\n- nextTask: \`WBV2-P12-004\`\n\nThe material and animation workflows are now bound to the compiled WebEngine WASM Main in Chromium. Unsupported material nodes remain explicit, and stale or malformed authoring commands do not mutate Main.\n`);
|
|
const previousPlan = readJson("tests/golden/WBV2-P12-002/domain-expansion-task-plan.json");
|
|
const nextPlan = structuredClone(previousPlan);
|
|
nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P12-002/domain-expansion-task-plan.json", sha256: sha256("tests/golden/WBV2-P12-002/domain-expansion-task-plan.json") };
|
|
nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P12-003" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P12-004" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false });
|
|
fs.writeFileSync(absolute(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`);
|
|
const nextCardPath = "docs/tasks/WBV2-P12-004.md";
|
|
fs.writeFileSync(absolute(nextCardPath), read(nextCardPath).replace("- status: planned", "- status: in_progress"));
|
|
execFileSync(process.execPath, [absolute("tools/web/generate-v2-task-index.mjs"), "--plan-path", absolute(planPath)], { cwd: root, stdio: "inherit" });
|
|
execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P12-003", "--write"], { cwd: root, stdio: "inherit" });
|
|
const artifactPaths = { parentManifest: parentPath, materialManifest: materialManifestPath, materialReport: materialReportPath, animationManifest: animationManifestPath, animationReport: animationReportPath, production: productionPath, productionSecond: secondProductionPath, e2e: "web/tests/e2e/wbv2-material-animation-production.spec.ts", client: "web/app/src/engine-client/WebEngineClient.ts", nativeApi: "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", generator: "tools/web/generate-wbv2-material-animation-receipt.mjs", report: reportPath, status: statusPath, parityPlan: planPath, taskContext: `${outputRoot}/task-context.json` };
|
|
const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }]));
|
|
const manifest = { schemaVersion: 1, task: "WBV2-P12-003", parentTask: "WBV2-P12-002", operation: "MATERIAL_ANIMATION_ENGINE_RECEIPT", status: "done", enablingTask: false, parityStateChange: true, runtime: "PLAYWRIGHT_CHROMIUM_WEBENGINE_WASM", artifacts, assertions: { materialMutations: 4, animationMutations: 2, saveReopen: "EXACT", negativeCases: 2, unsupportedGraphExplicit: true, deterministic: true, historicalReproduction: false }, nextTask: "WBV2-P12-004" };
|
|
fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
let queue = read("docs/EXECUTION_QUEUE.md");
|
|
queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P12-004` |")
|
|
.replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P12-003/manifest.json` |")
|
|
.replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P12-004.md`](tasks/WBV2-P12-004.md) |");
|
|
fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue);
|
|
let nextTaskFile = read("nextTask.md");
|
|
nextTaskFile = nextTaskFile.replace(/\| Current task when this file was written \| `[^`]+` \|/u, "| Current task when this file was written | `WBV2-P12-004` |")
|
|
.replace(/\| Parent manifest \| `[^`]+` \|/u, "| Parent manifest | `tests/golden/WBV2-P12-003/manifest.json` |")
|
|
.replace(/\| Current task card \| `[^`]+` \|/u, "| Current task card | `docs/tasks/WBV2-P12-004.md` |")
|
|
.replace(/\| Current task-context nextTask \| `[^`]+` \|/u, "| Current task-context nextTask | `WBV2-P12-005` |")
|
|
.replace(/\| Focused command \| `[^`]+` \|/u, "| Focused command | `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-viewport-nonmesh-production.spec.ts` |");
|
|
fs.writeFileSync(absolute("nextTask.md"), nextTaskFile);
|
|
process.stdout.write(`wbv2-material-animation-receipt-written task=WBV2-P12-003 material=EXACT animation=EXACT saveReopen=EXACT next=WBV2-P12-004 artifacts=${Object.keys(artifacts).length}\n`);
|