Files
FreeCADWasmDev/tools/assembly-worker-lifecycle-test.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

72 lines
3.0 KiB
JavaScript

import assert from "node:assert/strict";
import fs from "node:fs";
import vm from "node:vm";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const ts = require("../web/node_modules/typescript");
const source = fs.readFileSync(new URL("../web/src/assembly.worker.ts", import.meta.url), "utf8");
const compiled = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 },
}).outputText;
const tick = () => new Promise(resolve => setImmediate(resolve));
function start() {
const messages = [], loads = [], revoked = [];
const scope = { postMessage: message => messages.push(message) };
const envelope = {
FacadeSerialQueue: class {},
createFailure: (requestId, code, message) => ({ kind: "error", requestId, error: { code, message } }),
};
const factory = () => new Promise((resolve, reject) => loads.push({ resolve, reject }));
let sequence = 0;
vm.runInNewContext(compiled, {
exports: {}, self: scope, Blob, Error,
fetch: async () => ({ ok: true, text: async () => "loader" }),
URL: { createObjectURL: () => `blob:test-${++sequence}`, revokeObjectURL: url => revoked.push(url) },
require: name => name.startsWith("blob:") ? { default: factory }
: name.includes("facade-envelope") ? envelope : {},
});
return { scope, messages, loads, revoked };
}
const success = start();
await tick();
assert.equal(success.loads.length, 1);
assert.equal(success.messages.length, 0, "ready waits for native initialization");
assert.equal(success.revoked.length, 0, "loader URL survives asynchronous factory initialization");
success.loads[0].resolve({});
await tick();
assert.equal(success.messages[0].kind, "ready");
assert.equal(success.revoked.length, 1);
await success.scope.onmessage({ data: { kind: "reset", requestId: "reset-1" } });
await tick();
assert.equal(success.messages.length, 1, "reset waits for its runtime");
success.loads[1].reject(new Error("missing wasm"));
await tick();
assert.equal(success.messages[1].requestId, "reset-1");
assert.equal(success.messages[1].error.code, "native_load_failed");
assert.equal(success.messages[1].error.message, "missing wasm");
const failure = start();
await tick();
failure.loads[0].reject(new Error("startup failed"));
await tick();
assert.equal(failure.messages.length, 1);
assert.equal(failure.messages[0].requestId, "startup");
assert.equal(failure.messages[0].error.code, "native_load_failed");
const reset = start();
await tick();
await reset.scope.onmessage({ data: { kind: "reset", requestId: "replacement" } });
await tick();
reset.loads[0].resolve({});
await tick();
assert.equal(reset.messages.length, 0, "obsolete initialization cannot announce readiness");
reset.loads[1].resolve({});
await tick();
assert.equal(reset.messages.length, 1);
assert.equal(reset.messages[0].kind, "reset");
assert.equal(reset.messages[0].requestId, "replacement");
console.log("Assembly worker lifecycle: initialization, failure, reset and stale completion passed");