/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/wpt/runner/runner.ts
303 строки
8 KB
Kenta Moriuchi
chore(wpt): upgrade deno_dom to fix silent empty test bundles (#35184)
13 июн 2026, 10:18
Не верифицирован
13 июн 2026, 10:18
7cce318
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. import { delay, join, ROOT_PATH, TextLineStream, toFileUrl, } from "../../../tools/util.js"; import { assert, denoBinary, ManifestTestOptions, runPy } from "./utils.ts"; import { DOMParser } from "jsr:@b-fuze/deno-dom@0.1.56/wasm"; export async function runWithTestUtil<T>( verbose: boolean, f: () => Promise<T>, ): Promise<T> { const proc = runPy([ "wpt", "serve", "--config", "../../../tests/wpt/runner/config.json", ], { stdout: verbose ? "inherit" : "piped", stderr: verbose ? "inherit" : "piped", }); const start = performance.now(); while (true) { await delay(1000); try { const req = await fetch("http://localhost:8000/"); await req.body?.cancel(); if (req.status == 200) { break; } } catch (_err) { // do nothing if this fails } const passedTime = performance.now() - start; if (passedTime > 15000) { try { proc.kill("SIGINT"); } catch { // Might have already died } await proc.status; throw new Error("Timed out while trying to start wpt test util."); } } if (verbose) console.log(`Started wpt test util.`); try { return await f(); } finally { if (verbose) console.log("Killing wpt test util."); try { proc.kill("SIGINT"); } catch { // Might have already died } await proc.status; } } export interface TestResult { cases: TestCaseResult[]; harnessStatus: TestHarnessStatus | null; duration: number; status: number; stderr: string; } export interface TestHarnessStatus { status: number; message: string | null; stack: string | null; } export interface TestCaseResult { name: string; passed: boolean; status: number; message: string | null; stack: string | null; } export async function runSingleTest( url: URL, _options: ManifestTestOptions, reporter: (result: TestCaseResult) => void, inspectBrk: boolean, timeouts: { long: number; default: number }, fileTimeout = 2 * 60_000, ): Promise<TestResult> { const result = await Promise.race([ runSingleTestInner(url, _options, reporter, inspectBrk, timeouts), new Promise<TestResult>((resolve) => setTimeout(() => { resolve({ cases: [], harnessStatus: null, duration: fileTimeout, status: 1, stderr: `test file timed out after ${fileTimeout / 1000}s\n`, }); }, fileTimeout) ), ]); return result; } async function runSingleTestInner( url: URL, _options: ManifestTestOptions, reporter: (result: TestCaseResult) => void, inspectBrk: boolean, timeouts: { long: number; default: number }, ): Promise<TestResult> { const timeout = _options.timeout === "long" ? timeouts.long : timeouts.default; const filename = url.pathname.substring( url.pathname.lastIndexOf("/") + 1, url.pathname.indexOf("."), ); const { title } = Object.fromEntries(_options.script_metadata || []); const bundle = await generateBundle(url); const tempFile = await Deno.makeTempFile({ prefix: "wpt-bundle-", suffix: ".js", }); let interval; try { await Deno.writeTextFile(tempFile, bundle); const startTime = new Date().getTime(); const args = [ "run", "-A", "--unstable-webgpu", "--unstable-net", "--v8-flags=--expose-gc", ]; if (inspectBrk) { args.push("--inspect-brk"); } args.push( "--enable-testing-features-do-not-use", "--location", url.toString(), "--cert", join(ROOT_PATH, `./tests/wpt/runner/certs/cacert.pem`), tempFile, "[]", ); const start = performance.now(); const proc = new Deno.Command(denoBinary(), { args, env: { NO_COLOR: "1", }, stdout: "null", stderr: "piped", }).spawn(); const cases = []; let stderr = ""; let harnessStatus = null; const lines = proc.stderr.pipeThrough(new TextDecoderStream()).pipeThrough( new TextLineStream(), ); interval = setInterval(() => { const passedTime = performance.now() - start; if (passedTime > timeout) { try { proc.kill("SIGINT"); } catch { // Race: proc may have terminated between the for-await loop // draining stderr and `clearInterval` running in `finally`. } } }, 1000); for await (const line of lines as AsyncIterable<string>) { if (line.startsWith("{")) { const data = JSON.parse(line); const result = { ...data, passed: data.status == 0 }; if (/^Untitled( \d+)?$/.test(result.name)) { result.name = `${title || filename}${result.name.slice(8)}`; } cases.push(result); reporter(result); } else if (line.startsWith("#$#$#{")) { harnessStatus = JSON.parse(line.slice(5)); } else { stderr += line + "\n"; } } const duration = new Date().getTime() - startTime; const { code } = await proc.status; return { status: code, harnessStatus, duration, cases, stderr, }; } finally { clearInterval(interval); await Deno.remove(tempFile); } } function getShim(test: string): string { const shim = []; shim.push("globalThis.window = globalThis;"); if (test.includes("streams/transferable")) { shim.push(` { const { port1, port2 } = new MessageChannel(); port2.addEventListener('message', (e) => { queueMicrotask(() => { globalThis.dispatchEvent(e); }); }); port2.start(); globalThis.postMessage = (message, targetOriginOrOptions, transfer) => { let options = targetOriginOrOptions; if (transfer || typeof targetOriginOrOptions === 'string') { options = { transfer }; } return port1.postMessage(message, options); }; } `); } return shim.join("\n"); } async function generateBundle(location: URL): Promise<string> { const res = await fetch(location); const body = await res.text(); const doc = new DOMParser().parseFromString(body, "text/html"); assert(doc, "document should have been parsed"); const scripts = doc.getElementsByTagName("script"); const title = doc.getElementsByTagName("title")[0]?.childNodes[0]?.nodeValue; const scriptContents = []; let inlineScriptCount = 0; if (title) { const url = new URL(`#${inlineScriptCount}`, location); inlineScriptCount++; scriptContents.push([ url.href, `globalThis.META_TITLE=${JSON.stringify(title)}`, ]); } const shim = getShim(location.pathname); for (const script of scripts) { const src = script.getAttribute("src"); if (src === "/resources/testharnessreport.js") { const url = toFileUrl( join(ROOT_PATH, "./tests/wpt/runner/testharnessreport.js"), ); const contents = await Deno.readTextFile(url); scriptContents.push([url.href, shim]); scriptContents.push([url.href, contents]); } else if (src) { const url = new URL(src, location); const res = await fetch(url); if (res.ok) { const contents = await res.text(); scriptContents.push([url.href, shim]); scriptContents.push([url.href, contents]); } } else { const url = new URL(`#${inlineScriptCount}`, location); inlineScriptCount++; scriptContents.push([url.href, shim]); scriptContents.push([url.href, script.textContent]); } } return scriptContents.map(([url, contents]) => ` (function() { const [_,err] = Deno[Deno.internal].core.evalContext(${ JSON.stringify(contents) }, ${JSON.stringify(url)}); if (err !== null) { throw err?.thrown; } })();`).join("\n"); }