import { expect, test } from "@playwright/test"; test("loads the selected pthread engine with shared memory and its worker pool", async ({ page }) => { const pthreadRequests: string[] = []; page.on("request", (request) => { const pathname = new URL(request.url()).pathname; if (pathname.startsWith("/vendor/blender/pthread/")) pthreadRequests.push(pathname); }); const documentResponse = await page.goto("/"); expect(documentResponse).not.toBeNull(); const headers = await documentResponse!.allHeaders(); expect(headers["cross-origin-opener-policy"]).toBe("same-origin"); expect(headers["cross-origin-embedder-policy"]).toBe("require-corp"); const result = await page.evaluate(async () => { const { detectBrowserCapabilities, selectWebEngineVariant } = await import("/src/platform/capabilities.ts"); const manifest = await fetch("/engine-manifest.json", { cache: "no-store" }).then((response) => response.json()); const selection = selectWebEngineVariant(manifest, "PTHREAD_REQUIRED", detectBrowserCapabilities()); if (!selection.selectedVariant) { return { selected: null, gate: selection.pthreadGate, shared: false, handle: 0, liveAfterDestroy: -1 }; } const variant = selection.selectedVariant; const wasmResponse = await fetch(variant.resources.wasm.url, { cache: "no-store" }); if (!wasmResponse.ok) throw new Error(`pthread WASM request failed: ${wasmResponse.status}`); const wasmBinary = await wasmResponse.arrayBuffer(); const digest = await crypto.subtle.digest("SHA-256", wasmBinary); const actualHash = [...new Uint8Array(digest)] .map((byte) => byte.toString(16).padStart(2, "0")) .join(""); if (actualHash !== variant.resources.wasm.sha256) throw new Error("pthread WASM hash mismatch"); const imported = await import(/* @vite-ignore */ variant.resources.js.url) as { default: (options: { wasmBinary: ArrayBuffer }) => Promise<{ HEAPU8: Uint8Array; _web_engine_create(): number; _web_engine_destroy(handle: number): void; _web_engine_get_live_handles(): number; PThread: { unusedWorkers: Worker[]; runningWorkers: Worker[]; terminateAllThreads(): void; }; }>; }; const module = await imported.default({ wasmBinary }); const handle = module._web_engine_create(); const shared = module.HEAPU8.buffer instanceof SharedArrayBuffer; const liveBeforeDestroy = module._web_engine_get_live_handles(); const poolWorkersBeforeDispose = module.PThread.unusedWorkers.length + module.PThread.runningWorkers.length; module._web_engine_destroy(handle); module.PThread.terminateAllThreads(); return { selected: variant.id, gate: selection.pthreadGate, shared, handle, liveBeforeDestroy, liveAfterDestroy: module._web_engine_get_live_handles(), poolWorkersBeforeDispose, poolWorkersAfterDispose: module.PThread.unusedWorkers.length + module.PThread.runningWorkers.length, }; }); expect(result).toMatchObject({ selected: "pthread", gate: { taskId: "M6-02", capability: "WASM_PTHREAD_ENGINE", status: "READY", issues: [] }, shared: true, liveBeforeDestroy: 1, liveAfterDestroy: 0, poolWorkersBeforeDispose: 1, poolWorkersAfterDispose: 0, }); expect(result.handle).toBeGreaterThan(0); expect(pthreadRequests.filter((url) => url.endsWith("/web_engine.wasm"))).toHaveLength(1); expect(pthreadRequests.filter((url) => url.endsWith("/web_engine.js")).length).toBeGreaterThanOrEqual(2); });