Files
FreeCADWasmDev/tools/run_p6_document_graph_family_gate.mjs
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

40 lines
5.2 KiB
JavaScript

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { createRequire } from "node:module";
import { fileURLToPath, pathToFileURL } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const build = path.resolve(process.argv[2] ?? path.join(root, "freecad-source/build-web/configure/bin"));
const p6 = path.join(root, "docs/p6");
const hash = (value) => crypto.createHash("sha256").update(typeof value === "string" ? value : JSON.stringify(value)).digest("hex");
const fileHash = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
const sourceRevision = execFileSync("git", ["-C", path.join(root, "freecad-source"), "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
globalThis.__dirname = `${build}${path.sep}`;
globalThis.require = createRequire(import.meta.url);
const module = await import(pathToFileURL(path.join(build, "FreeCADWebDocumentGraphFamilyProbe.js")));
const kernel = await module.default({ wasmBinary: fs.readFileSync(path.join(build, "FreeCADWebDocumentGraphFamilyProbe.wasm")) });
const dispatch = (request) => JSON.parse(String(kernel.ccall("fc_web_dispatch_json", "string", ["string"], [JSON.stringify(request)])));
const request = { cmd: "documentGraph.family", args: { objectTypes: 13 } };
const first = dispatch(request);
const second = dispatch(request);
const semantic = (value) => ({ ...value, records: value.records?.map(({ name, status, detail }) => ({ name, status, detail })) });
const replayStable = JSON.stringify(semantic(first)) === JSON.stringify(semantic(second));
if (first.ok !== true || first.inventory !== 13 || first.records?.length !== 13 || !replayStable || first.fixture?.semanticRoundTrip !== true) throw new Error(`document graph replay failed: ${JSON.stringify({ first, second, replayStable })}`);
const records = first.records.map((record) => ({ ...record, inputSha256: hash({ cmd: request.cmd, args: request.args, object: record.name }), resultSha256: hash({ name: record.name, status: record.status, detail: record.detail }) }));
const counts = records.reduce((result, record) => { result[record.status] = (result[record.status] ?? 0) + 1; return result; }, {});
const invalidRequest = dispatch({ cmd: "documentGraph.family" });
const unsupportedCommand = dispatch({ cmd: "documentGraph.unknown", args: {} });
const recovery = { invalidRequest: { code: invalidRequest.code, rejected: invalidRequest.ok === false }, unsupportedCommand: { code: unsupportedCommand.code, rejected: unsupportedCommand.ok === false }, resourcesReleased: first.resourcesReleased === true, semanticRoundTrip: first.fixture.semanticRoundTrip === true };
if (invalidRequest.code !== "invalid_request" || unsupportedCommand.code !== "unsupported_command" || !recovery.resourcesReleased) throw new Error(`document graph recovery failed: ${JSON.stringify(recovery)}`);
const source = { revision: sourceRevision, probe: "FreeCADWebDocumentGraphFamilyProbe", js: "freecad-source/build-web/configure/bin/FreeCADWebDocumentGraphFamilyProbe.js", wasm: "freecad-source/build-web/configure/bin/FreeCADWebDocumentGraphFamilyProbe.wasm", jsSha256: fileHash(path.join(build, "FreeCADWebDocumentGraphFamilyProbe.js")), wasmSha256: fileHash(path.join(build, "FreeCADWebDocumentGraphFamilyProbe.wasm")) };
const kernelReport = { schema: "freecad-web-p6-document-graph-family-kernel/v1", taskId: "FW-P6-FPA-D5", result: "pass", source, request, inputSha256: hash(request), replay: { stable: replayStable, first: semantic(first), second: semantic(second), resultSha256: hash(semantic(first)) }, fixture: first.fixture, counts: { inventory: records.length, reported: records.length, passed: counts.passed ?? 0, passedBoundary: counts.passed_boundary ?? 0, blocked: counts.blocked ?? 0, resourcesReleased: recovery.resourcesReleased }, records, recovery, boundaries: ["App::Link + Part::Box + App::DocumentObjectGroup are created, linked, recomputed, and restored by the native Document actor.", "Module-specific Assembly/Fem/Import/Inspection/TechDraw graph classes remain explicit passed_boundary records when their libraries are not linked into this GUI-free probe."] };
fs.mkdirSync(p6, { recursive: true });
fs.writeFileSync(path.join(p6, "document-graph-family-kernel.json"), `${JSON.stringify(kernelReport, null, 2)}\n`);
const gate = { schema: "freecad-web-p6-document-graph-family-gate/v1", taskId: "FW-P6-FPA-D5", result: "pass_with_boundaries", coverage: { inventoryRecords: 13, probedRecords: records.length, counts: kernelReport.counts }, fixture: first.fixture, records, source: { kernel: "docs/p6/document-graph-family-kernel.json", kernelSha256: fileHash(path.join(p6, "document-graph-family-kernel.json")) }, recovery, boundaries: kernelReport.boundaries };
fs.writeFileSync(path.join(p6, "document-graph-family-gate.json"), `${JSON.stringify(gate, null, 2)}\n`);
fs.writeFileSync(path.join(root, "docs/freecad-p6-document-graph-family-gate.log"), `${JSON.stringify({ result: gate.result, counts: gate.coverage.counts, fixture: gate.fixture, recovery })}\n`);
console.log(JSON.stringify({ result: gate.result, counts: gate.coverage.counts, fixture: gate.fixture, inputSha256: kernelReport.inputSha256 }));