mirror of
https://github.com/denoland/deno.git
synced 2025-12-23 08:48:24 +00:00
https://streams.spec.whatwg.org/#rs-transfer https://streams.spec.whatwg.org/#ws-transfer https://streams.spec.whatwg.org/#ts-transfer Remaining test failures are due to our `DOMException` not correctly being serializable and can be solved in a followup. ```js // example const INDEX_HTML = Deno.readTextFileSync("./index.html"); const worker = new Worker("./the_algorithm.js", { type: "module" }); Deno.serve(async (req) => { if (req.method === "POST" && req.path === "/the-algorithm") { const { port1, port2 } = new MessageChannel(); worker.postMessage({ stream: req.body, port: port1 }, { transfer: [req.body, port1] }); const res = await new Promise((resolve) => { port1.onmessage = (e) => resolve(e.data); }); return new Response(res); } if (req.path === "/") { return new Response(INDEX_HTML, { "content-type": "text/html" }); } return new Response(null, { status: 404 }); }); ``` --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
273 lines
6.9 KiB
TypeScript
273 lines
6.9 KiB
TypeScript
// Copyright 2018-2025 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 "https://deno.land/x/deno_dom@v0.1.3-alpha2/deno-dom-wasm.ts";
|
|
|
|
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 },
|
|
): 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) {
|
|
proc.kill("SIGINT");
|
|
}
|
|
}, 1000);
|
|
for await (const line of lines) {
|
|
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");
|
|
}
|