import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import http from "node:http"; import os from "node:os"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const distRoot = path.join(root, "web/dist"); const reportPath = path.join(root, "tests/golden/M14-01D/probe-identity-report.json"); const manifestPath = path.join(root, "tests/golden/M14-01D/manifest.json"); const digest = (value) => crypto.createHash("sha256").update(value).digest("hex"); const fileDigest = (file) => digest(fs.readFileSync(file)); const relative = (file) => path.relative(root, file).replaceAll(path.sep, "/"); const assetsRoot = path.join(distRoot, "assets"); assert.ok(fs.existsSync(assetsRoot), "production assets are missing; run npm --prefix web run build first"); const workerName = fs.readdirSync(assetsRoot).find((name) => /^storage\.worker-[\w-]+\.js$/u.test(name)); const wasmName = fs.readdirSync(assetsRoot).find((name) => /^web_engine-[\w-]+\.wasm$/u.test(name)); assert.ok(workerName && wasmName, "production worker/WASM assets are missing"); const mime = new Map([ [".html", "text/html; charset=utf-8"], [".js", "text/javascript; charset=utf-8"], [".css", "text/css; charset=utf-8"], [".json", "application/json"], [".wasm", "application/wasm"], ]); const server = http.createServer((request, response) => { const pathname = decodeURIComponent(new URL(request.url ?? "/", "http://127.0.0.1").pathname); const relativePath = pathname === "/" ? "index.html" : pathname.replace(/^\//u, ""); const file = path.resolve(distRoot, relativePath); if (!file.startsWith(`${distRoot}${path.sep}`) || !fs.existsSync(file) || !fs.statSync(file).isFile()) { response.writeHead(404); response.end("not found"); return; } response.statusCode = 200; response.setHeader("Content-Type", mime.get(path.extname(file)) ?? "application/octet-stream"); response.setHeader("Cross-Origin-Opener-Policy", "same-origin"); response.setHeader("Cross-Origin-Embedder-Policy", "require-corp"); response.setHeader("Cross-Origin-Resource-Policy", "same-origin"); response.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; connect-src 'self'; font-src 'self'; img-src 'self'; media-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'"); fs.createReadStream(file).pipe(response); }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const address = server.address(); assert.ok(address && typeof address === "object"); const origin = `http://127.0.0.1:${address.port}`; let browser; try { const playwright = await import(pathToFileURL(path.join(root, "web/node_modules/playwright/index.mjs")).href); browser = await playwright.chromium.launch({ headless: true }); const context = await browser.newContext(); const page = await context.newPage(); await page.goto(`${origin}/`, { waitUntil: "load" }); const probe = await page.evaluate(async ({ workerPath, wasmPath }) => { const run = async (name, operation) => { try { return { name, ...await operation() }; } catch (error) { return { name, status: "BLOCKED", code: error instanceof Error ? error.name : "PROBE_FAILED", detail: error instanceof Error ? error.message.slice(0, 200) : String(error), }; } }; const wasm = await run("wasm", async () => { const response = await fetch(wasmPath); if (!response.ok) throw new Error(`HTTP_${response.status}`); const bytes = await response.arrayBuffer(); await WebAssembly.compile(bytes); return { status: "PASS", code: "WASM_READY", bytes: bytes.byteLength }; }); const worker = await run("worker", async () => { await new Promise((resolve, reject) => { const value = new Worker(workerPath, { type: "module" }); const timer = setTimeout(() => { value.terminate(); resolve(); }, 500); value.onerror = (event) => { clearTimeout(timer); value.terminate(); reject(new Error(event.message || "WORKER_LOAD_FAILED")); }; }); return { status: "PASS", code: "WORKER_READY" }; }); const webgl2 = await run("webgl2", async () => { const canvas = document.createElement("canvas"); const gl = canvas.getContext("webgl2"); if (!gl) return { status: "BLOCKED", code: "WEBGL2_UNAVAILABLE" }; const debug = gl.getExtension("WEBGL_debug_renderer_info"); return { status: "PASS", code: "WEBGL2_READY", vendor: debug ? gl.getParameter(debug.UNMASKED_VENDOR_WEBGL) : "REDACTED", renderer: debug ? gl.getParameter(debug.UNMASKED_RENDERER_WEBGL) : "REDACTED", version: gl.getParameter(gl.VERSION), }; }); const webgpu = await run("webgpu", async () => { if (!("gpu" in navigator)) return { status: "BLOCKED", code: "WEBGPU_UNAVAILABLE" }; const adapter = await navigator.gpu.requestAdapter({ powerPreference: "high-performance" }); if (!adapter) return { status: "BLOCKED", code: "WEBGPU_ADAPTER_UNAVAILABLE" }; const info = adapter.info ?? {}; return { status: "PASS", code: "WEBGPU_READY", vendor: info.vendor ?? "REDACTED", architecture: info.architecture ?? "REDACTED", device: info.device ?? "REDACTED", description: info.description ?? "REDACTED", isFallbackAdapter: Boolean(adapter.isFallbackAdapter), }; }); return { userAgent: navigator.userAgent, platform: navigator.platform, language: navigator.language, hardwareConcurrency: navigator.hardwareConcurrency, deviceMemory: typeof navigator.deviceMemory === "number" ? navigator.deviceMemory : null, crossOriginIsolated, capabilities: [wasm, worker, webgl2, webgpu], }; }, { workerPath: `/assets/${workerName}`, wasmPath: `/assets/${wasmName}` }); const capabilities = Object.fromEntries(probe.capabilities.map((item) => [item.name, item])); const reportWithoutIdentity = { schemaVersion: 1, task: "M14-01D", operation: "PROBE_IDENTITY_GPU_OS_ADAPTER", runtime: "PLAYWRIGHT_CHROMIUM", browser: { version: await browser.version(), executablePath: playwright.chromium.executablePath(), userAgent: probe.userAgent, platform: probe.platform, language: probe.language, hardwareConcurrency: probe.hardwareConcurrency, deviceMemory: probe.deviceMemory, }, os: { platform: os.platform(), release: os.release(), version: os.version(), arch: os.arch(), machine: os.machine(), cpus: os.cpus().length, }, adapter: { webgl2: capabilities.webgl2, webgpu: capabilities.webgpu, }, isolation: { crossOriginIsolated: probe.crossOriginIsolated }, assets: { worker: { path: relative(path.join(assetsRoot, workerName)), sha256: fileDigest(path.join(assetsRoot, workerName)) }, wasm: { path: relative(path.join(assetsRoot, wasmName)), sha256: fileDigest(path.join(assetsRoot, wasmName)) }, }, supportRule: "IDENTITY_IS_OBSERVED_ONLY; PASS_ONLY_WHEN_PROBED; BLOCKED_OR_UNAVAILABLE_DOES_NOT_CLAIM_SUPPORT", execution: "DISABLED", nextTask: "M14-01E", }; const identityInput = JSON.stringify(reportWithoutIdentity); const report = { ...reportWithoutIdentity, identitySha256: digest(identityInput) }; if (process.env.UPDATE_M14_01D_REPORT === "1") { fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); if (process.env.UPDATE_M14_01D_REPORT === "1") { const artifactPaths = { parentManifest: path.join(root, "tests/golden/M14-01C/manifest.json"), checker: path.join(root, "tools/web/check-probe-identity.mjs"), package: path.join(root, "web/package.json"), engineManifest: path.join(root, "web/app/public/engine-manifest.json"), report: reportPath, }; const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: relative(file), sha256: fileDigest(file) }])); fs.writeFileSync(manifestPath, `${JSON.stringify({ schemaVersion: 1, task: "M14-01D", parentTask: "M14-01C", enablingTask: false, parityStateChange: false, runtime: "PLAYWRIGHT_CHROMIUM", operation: "PROBE_IDENTITY_GPU_OS_ADAPTER", artifacts, nextTask: "M14-01E" }, null, 2)}\n`); } } assert.deepEqual(JSON.parse(fs.readFileSync(reportPath, "utf8")), report); const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); assert.deepEqual({ schemaVersion: manifest.schemaVersion, task: manifest.task, parentTask: manifest.parentTask, nextTask: manifest.nextTask }, { schemaVersion: 1, task: "M14-01D", parentTask: "M14-01C", nextTask: "M14-01E" }); for (const artifact of Object.values(manifest.artifacts)) assert.equal(fileDigest(path.join(root, artifact.path)), artifact.sha256, artifact.path); assert.equal(report.identitySha256, digest(JSON.stringify(reportWithoutIdentity))); const summary = [capabilities.webgl2, capabilities.webgpu].map((item) => `${item.name}=${item.status}`).join(","); process.stdout.write(`probe-identity-ok version=${report.browser.version} os=${report.os.platform}/${report.os.arch} ${summary} identity=${report.identitySha256} execution=DISABLED next=${manifest.nextTask}\n`); } finally { await browser?.close(); await new Promise((resolve) => server.close(resolve)); }