Files
FreeCADWasmDev/tools/run_p6_bim_python_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

101 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { createRequire } from "node:module";
import { fileURLToPath, pathToFileURL } from "node:url";
import { execFileSync } from "node:child_process";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const source = path.join(root, "freecad-source");
const build = path.resolve(process.argv[2] ?? path.join(source, "build-web/configure/bin"));
const docs = path.join(root, "docs/p6");
const manifest = JSON.parse(fs.readFileSync(path.join(docs, "python-api-manifest.json"), "utf8"));
const records = manifest.records
.filter((row) => row.module === "BIM" && row.tier === "tier-1-readonly")
.map((row) => ({ apiId: row.id, name: row.name, family: row.family }));
const command = "bim.pythonFamilyBatch";
const schema = "freecad-web-bim/python-family-batch/v1";
const loaderPath = path.join(build, "FreeCADWebBimRuntimeProbe.js");
globalThis.__dirname = `${build}${path.sep}`;
globalThis.require = createRequire(import.meta.url);
process.chdir(path.dirname(build));
const loader = await import(pathToFileURL(loaderPath));
const kernel = await loader.default({ wasmBinary: fs.readFileSync(path.join(build, "FreeCADWebBimRuntimeProbe.wasm")), locateFile: (file) => path.join(build, file) });
const dispatch = (request) => JSON.parse(String(kernel.ccall("fc_web_dispatch_json", "string", ["string"], [JSON.stringify(request)])));
const request = { cmd: command, args: { tier: "tier-1-readonly", records } };
const first = dispatch(request);
const second = dispatch(request);
const invalid = dispatch({ cmd: command, args: {} });
const wrongTier = dispatch({ cmd: command, args: { tier: "tier-2-pure", records: records.slice(0, 1) } });
const unsupported = dispatch({ cmd: "bim.unknown", args: { records } });
const stable = (value) => crypto.createHash("sha256").update(JSON.stringify(value)).digest("hex");
const counts = Array.isArray(first.records)
? first.records.reduce((acc, item) => { acc[item.status] = (acc[item.status] ?? 0) + 1; return acc; }, {})
: {};
const recordEvidence = Array.isArray(first.records)
? first.records.map((item) => ({
apiId: item.apiId,
name: item.name,
family: item.family,
disposition: item.status,
evidenceSha256: stable({ command, tier: "tier-1-readonly", record: item }),
}))
: [];
const idsMatch = Array.isArray(first.records) && first.records.every((item, index) => item.apiId === records[index]?.apiId && item.name === records[index]?.name && item.family === records[index]?.family);
const result = first.ok === true
&& first.schema === schema
&& first.module === "BIM"
&& first.tier === "tier-1-readonly"
&& first.source === "freecad-source"
&& first.count === records.length
&& records.length === 824
&& first.records.length === records.length
&& counts.restricted === 239
&& counts.not_implemented === 585
&& new Set(first.records.map((item) => item.apiId)).size === records.length
&& idsMatch
&& first.resourcesReleased === true
&& first.records.every((item) => item.native === true && ["restricted", "not_implemented"].includes(item.status))
&& stable(first) === stable(second)
&& invalid.ok === false
&& invalid.code === "invalid_request"
&& wrongTier.ok === false
&& wrongTier.code === "invalid_request"
&& unsupported.ok === false
&& unsupported.code === "unsupported_command";
const report = {
schema: "freecad-web-p6-bim-tier1-readonly-native/v1",
taskId: "FW-P6-FPA-F1-BIM",
result: result ? "pass_with_boundaries" : "fail",
sourceRevision: execFileSync("git", ["-C", source, "rev-parse", "HEAD"], { encoding: "utf8" }).trim(),
dispatcher: "fc_web_dispatch_json",
command,
requestSchema: schema,
coverage: {
manifestRecords: records.length,
returnedRecords: Array.isArray(first.records) ? first.records.length : 0,
statusCounts: counts,
uniqueIds: Array.isArray(first.records) ? new Set(first.records.map((item) => item.apiId)).size : 0,
idsMatch,
},
records: recordEvidence,
response: { firstSha256: stable(first), secondSha256: stable(second), identical: stable(first) === stable(second), resourcesReleased: first.resourcesReleased === true },
errors: { invalid, wrongTier, unsupported },
artifacts: {
wasm: "web/public/kernel/FreeCADWebBimRuntimeProbe.wasm",
loader: "web/public/kernel/FreeCADWebBimRuntimeProbe.js",
data: "web/public/kernel/FreeCADWebBimRuntimeProbe.data",
},
boundaries: [
"BIM view-resource APIs are restricted because GUI providers are unavailable in the GUI-free Web kernel.",
"Generic-object and geometry-value records remain not_implemented until method-level readonly parameter/return evidence is available.",
"Each disposition is derived from the native FreeCAD batch response; JavaScript only hashes and validates the response.",
],
};
fs.mkdirSync(docs, { recursive: true });
fs.writeFileSync(path.join(docs, "bim-tier1-readonly-native.json"), `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(path.join(root, "docs/freecad-p6-bim-f1-readonly.log"), `${JSON.stringify({ result: report.result, coverage: report.coverage, firstSha256: report.response.firstSha256, invalidCode: invalid.code, wrongTierCode: wrongTier.code, unsupportedCode: unsupported.code })}\n`);
console.log(JSON.stringify({ result: report.result, coverage: report.coverage, invalidCode: invalid.code, wrongTierCode: wrongTier.code, unsupportedCode: unsupported.code }));
if (!result) process.exitCode = 1;