Files
FreeCADWasmDev/tools/test-partdesign-edge-identity.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

67 lines
3.8 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { Worker, isMainThread, parentPort } from "node:worker_threads";
import { loadWasmProbe } from "./wasm-probe-runtime.mjs";
async function runNativeRegression() {
const { kernel } = await loadWasmProbe(process.env.FREECAD_PARTDESIGN_PROFILE_BUSINESS_BUILD ?? "web/public/kernel", "FreeCADWebPartDesignRuntimeProbe");
const document = "EdgeIdentityRegression", path = "/tmp/EdgeIdentityRegression.FCStd";
const timings = [];
const call = args => {
const start = performance.now();
const result = JSON.parse(kernel.ccall("fc_web_dispatch_json", "string", ["string"], [JSON.stringify({ cmd: "partdesign.document", args: { document, body: "Body", ...args } })]));
timings.push({ action: args.action, seconds: Number(((performance.now() - start) / 1000).toFixed(4)) });
assert.equal(result.ok, true, JSON.stringify(result));
return result.result;
};
const sketch = { action: "setSketch", object: "Sketch", profileType: "Rectangle", parameters: { Width: 4, Height: 8 }, position: [0, 0, 0] };
const pad = { action: "setFeature", object: "Pad", profile: "Sketch", featureType: "Pad", length: 6 };
const get = (state, name) => state.objects.find(object => object.name === name);
const near = (actual, expected) => assert.ok(Math.abs(actual - expected) < 1e-7, `${actual} != ${expected}`);
const expectedVolume = (width, height, length) => width * height * length - (1 - Math.PI / 4) * 0.5 ** 2 * length;
try {
call({ action: "create" }); call({ action: "createBody", object: "Body" }); call(sketch); call(pad);
const first = get(call({ action: "snapshot", trace: true }), "Pad").mappedEdges;
assert.equal(first.length, 12);
call({ action: "setDressUp", object: "Fillet", dressUpType: "Fillet", base: "Pad", edges: ["Edge1"], size: 0.5 });
for (const length of [8, 3.5, 12, 6]) {
const edited = call({ ...pad, length, trace: true });
assert.deepEqual(get(edited, "Pad").mappedEdges, first);
near(get(edited, "Fillet").volume, expectedVolume(4, 8, length));
call({ action: "undo" });
const redone = call({ action: "redo", trace: true });
assert.deepEqual(redone.objects, edited.objects);
}
for (const [width, height] of [[5, 9], [3, 6], [4, 8]]) {
const edited = call({ ...sketch, parameters: { Width: width, Height: height }, trace: true });
assert.deepEqual(get(edited, "Pad").mappedEdges, first);
assert.equal(get(edited, "Sketch").dof, 0);
near(get(edited, "Fillet").volume, expectedVolume(width, height, 6));
}
const saved = call({ action: "save", path }); call({ action: "close" });
const reopened = call({ action: "open", path }); assert.deepEqual(reopened.objects, saved.objects);
const edited = call({ ...pad, length: 9, trace: true });
assert.deepEqual(get(edited, "Pad").mappedEdges, first);
near(get(edited, "Fillet").volume, expectedVolume(4, 8, 9));
return timings;
} finally {
try { call({ action: "close" }); } finally { try { kernel.FS.unlink(path); } catch {} }
}
}
if (isMainThread) {
test("native edge identity survives repeated profile edits, base undo/redo and FCStd", async () => {
const worker = new Worker(new URL(import.meta.url));
try {
const result = await new Promise((resolve, reject) => {
worker.once("message", resolve); worker.once("error", reject);
worker.once("exit", code => reject(new Error(`Native regression exited without a result: ${code}`)));
});
assert.equal(result.error, undefined, result.error);
console.log(JSON.stringify({ nativeEdgeIdentityTimings: result.timings }));
} finally { await worker.terminate(); }
});
} else {
runNativeRegression().then(timings => parentPort.postMessage({ timings }), error => parentPort.postMessage({ error: String(error.stack ?? error) }));
}