/** * Headless GPU test runner: serves dist/, opens test.html in headless Chrome * (hardware WebGPU if there is any, the SwiftShader software adapter * otherwise), and reports the suite's results. * * Run after `vite build`: node scripts/test-gpu.mjs [?m=2&norder=4&k=4] */ import { createServer } from 'node:http'; import { readFile } from 'node:fs/promises'; import { extname, join } from 'node:path'; import puppeteer from 'puppeteer-core'; const DIST = new URL('../dist/', import.meta.url).pathname; const CHROME = process.env.CHROME_PATH ?? '/usr/bin/google-chrome'; const QUERY = process.argv[2] ?? ''; const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', }; const server = createServer(async (req, res) => { try { const path = req.url === '/' ? '/index.html' : req.url.split('?')[0]; const data = await readFile(join(DIST, path)); res.writeHead(200, { 'content-type': MIME[extname(path)] ?? 'application/octet-stream' }); res.end(data); } catch { res.writeHead(404); res.end('not found'); } }); await new Promise((r) => server.listen(0, '127.0.0.1', r)); const port = server.address().port; const flagSets = [ ['--headless=new', '--no-sandbox', '--enable-unsafe-webgpu', '--enable-features=Vulkan'], ['--headless=new', '--no-sandbox', '--enable-unsafe-webgpu', '--use-webgpu-adapter=swiftshader', '--enable-unsafe-swiftshader'], ]; if (process.env.SWIFTSHADER_ONLY) flagSets.shift(); let final = null; for (const flags of flagSets) { const browser = await puppeteer.launch({ executablePath: CHROME, args: flags, protocolTimeout: 1_800_000 }); try { const page = await browser.newPage(); page.on('console', (msg) => { const t = msg.text(); if (!/^(ok|FAIL|surface|device|operator|FATAL|\s)/.test(t)) console.log(` [page] ${t}`); else console.log(t); }); page.on('pageerror', (err) => console.log(` [pageerror] ${err.message}`)); await page.goto(`http://127.0.0.1:${port}/test.html${QUERY}`, { waitUntil: 'load' }); const results = await page.waitForFunction(() => window.__RESULTS__, { timeout: 1_800_000, polling: 500 }); final = await results.jsonValue(); } catch (e) { console.error(`run with flags [${flags.join(' ')}] failed: ${e.message}`); } finally { await browser.close(); } if (final && !final.fatal) break; if (final?.fatal) console.log(`fatal: ${final.fatal}`); console.log('retrying with next flag set…'); } server.close(); if (!final) { console.error('no results'); process.exit(1); } console.log(`\n${final.passed} passed, ${final.failed} failed`); process.exit(final.failed ? 1 : 0);