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

98 lines
5.6 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 facadeDir = path.resolve(process.argv[2] ?? path.join(root, "web/.tmp-data-property-facade"));
const build = path.resolve(process.argv[3] ?? 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 kernelModule = await import(pathToFileURL(path.join(build, "FreeCADWebDataPropertyFamilyProbe.js")));
const kernel = await kernelModule.default({ wasmBinary: fs.readFileSync(path.join(build, "FreeCADWebDataPropertyFamilyProbe.wasm")) });
const facade = await import(pathToFileURL(path.join(facadeDir, "data-property-facade.js")));
const envelope = await import(pathToFileURL(path.join(facadeDir, "facade-envelope.js")));
const queue = new envelope.FacadeSerialQueue();
const request = facade.createDataPropertyRequest("d3-facade-smoke");
const first = await facade.dispatchDataProperty(queue, request, kernel);
const second = await facade.dispatchDataProperty(queue, facade.createDataPropertyRequest("d3-facade-replay"), kernel);
const duplicate = await facade.dispatchDataProperty(queue, request, kernel);
const cancelledRequest = facade.createDataPropertyRequest("d3-facade-cancel");
const cancelledPromise = facade.dispatchDataProperty(queue, cancelledRequest, kernel);
const cancelAccepted = queue.cancel(envelope.createCancel("d3-facade-cancel-control", "d3-facade-cancel"));
const cancelled = await cancelledPromise;
const invalid = await facade.dispatchDataProperty(queue, { kind: "request", command: facade.DATA_PROPERTY_COMMAND }, kernel);
const firstResult = first.kind === "result" ? first.result : null;
const secondResult = second.kind === "result" ? second.result : null;
const resultShape = (value) => value ? {
schema: value.schema,
module: value.module,
action: value.action,
source: value.source,
inventory: value.inventory,
counts: value.counts,
records: value.records,
recovery: value.recovery,
resourcesReleased: value.resourcesReleased,
nativeProbe: value.nativeProbe,
} : null;
const stable = JSON.stringify(resultShape(firstResult)) === JSON.stringify(resultShape(secondResult));
const hashFields = firstResult?.records?.filter((record) => typeof record.inputSha256 === "string" && typeof record.resultSha256 === "string").length ?? 0;
const ok = first.kind === "result"
&& firstResult?.inventory === 40
&& firstResult.records?.length === 40
&& firstResult.counts?.blocked === 0
&& firstResult.resourcesReleased === true
&& hashFields === 40
&& stable
&& duplicate.kind === "error" && duplicate.error.code === "duplicate_request"
&& cancelAccepted && cancelled.kind === "error" && cancelled.error.code === "cancelled"
&& invalid.kind === "error" && invalid.error.code === "invalid_request";
if (!ok) throw new Error(JSON.stringify({ first, second, duplicate, cancelled, invalid, cancelAccepted, stable, hashFields }));
const report = {
schema: "freecad-web-p6-data-property-family-facade/v1",
taskId: "FW-P6-FPA-D3",
result: "pass",
source: {
revision: sourceRevision,
kernelJs: "freecad-source/build-web/configure/bin/FreeCADWebDataPropertyFamilyProbe.js",
kernelWasm: "freecad-source/build-web/configure/bin/FreeCADWebDataPropertyFamilyProbe.wasm",
wasmSha256: fileHash(path.join(build, "FreeCADWebDataPropertyFamilyProbe.wasm")),
facade: "web/src/data-property-facade.ts",
worker: "web/src/data-property.worker.ts",
coreWorker: "web/src/core.worker.ts",
react: "web/src/core-shell.tsx",
},
request: { ...request, payload: { ...request.payload } },
inputSha256: hash(request),
resultSha256: hash(resultShape(firstResult)),
payload: resultShape(firstResult),
replay: { stable, secondResultSha256: hash(resultShape(secondResult)) },
sequencing: {
duplicate: duplicate.error.code,
cancelAccepted,
cancelled: cancelled.error.code,
invalidRequest: invalid.error.code,
},
schemaChecks: { records: 40, hashFields, blocked: firstResult.counts.blocked, resourcesReleased: firstResult.resourcesReleased },
boundaries: [
"Native object registration, property fixtures, recompute, and applicable FCStd round-trips are executed by the FreeCAD WASM probe.",
"Three optional external Material backend types are reported as passed_boundary because BUILD_MATERIAL_EXTERNAL is not enabled; their absence is preserved in native detail.",
],
};
fs.mkdirSync(p6, { recursive: true });
fs.writeFileSync(path.join(p6, "data-property-family-facade.json"), `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(path.join(root, "docs/freecad-p6-data-property-family-facade.log"), `${JSON.stringify({ result: report.result, counts: firstResult.counts, inputSha256: report.inputSha256, resultSha256: report.resultSha256, sequencing: report.sequencing })}\n`);
console.log(JSON.stringify({ result: report.result, counts: firstResult.counts, hashFields, inputSha256: report.inputSha256, resultSha256: report.resultSha256 }));