98 lines
5.6 KiB
JavaScript
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 }));
|