72 lines
3.0 KiB
JavaScript
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");
|