From 8c6d147e6ae40a1f92aba1aedc0d95ef437dd4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 15 Feb 2021 14:48:47 +0100 Subject: chore: Reorganise workers tests (#9493) --- cli/tests/073_worker_error.ts | 5 - cli/tests/073_worker_error.ts.out | 7 - cli/tests/074_worker_nested_error.ts | 5 - cli/tests/074_worker_nested_error.ts.out | 7 - cli/tests/integration_tests.rs | 26 +- cli/tests/subdir/worker_error.ts | 5 - cli/tests/subdir/worker_location.ts | 6 - cli/tests/subdir/worker_types.ts | 4 - cli/tests/unit/worker_types.ts | 2 +- cli/tests/worker_post_undefined.ts | 5 - cli/tests/workers/bench_round_robin.ts | 65 +++ cli/tests/workers/bench_startup.ts | 33 ++ cli/tests/workers/error.ts | 5 + cli/tests/workers/post_undefined.ts | 5 + cli/tests/workers/test.ts | 677 ++++++++++++++++++++++++++ cli/tests/workers/worker_error.ts | 5 + cli/tests/workers/worker_error.ts.out | 7 + cli/tests/workers/worker_location.ts | 6 + cli/tests/workers/worker_nested_error.ts | 5 + cli/tests/workers/worker_nested_error.ts.out | 7 + cli/tests/workers/worker_types.ts | 4 + cli/tests/workers_round_robin_bench.ts | 65 --- cli/tests/workers_startup_bench.ts | 33 -- cli/tests/workers_test.ts | 678 --------------------------- 24 files changed, 833 insertions(+), 834 deletions(-) delete mode 100644 cli/tests/073_worker_error.ts delete mode 100644 cli/tests/073_worker_error.ts.out delete mode 100644 cli/tests/074_worker_nested_error.ts delete mode 100644 cli/tests/074_worker_nested_error.ts.out delete mode 100644 cli/tests/subdir/worker_error.ts delete mode 100644 cli/tests/subdir/worker_location.ts delete mode 100644 cli/tests/subdir/worker_types.ts delete mode 100644 cli/tests/worker_post_undefined.ts create mode 100644 cli/tests/workers/bench_round_robin.ts create mode 100644 cli/tests/workers/bench_startup.ts create mode 100644 cli/tests/workers/error.ts create mode 100644 cli/tests/workers/post_undefined.ts create mode 100644 cli/tests/workers/test.ts create mode 100644 cli/tests/workers/worker_error.ts create mode 100644 cli/tests/workers/worker_error.ts.out create mode 100644 cli/tests/workers/worker_location.ts create mode 100644 cli/tests/workers/worker_nested_error.ts create mode 100644 cli/tests/workers/worker_nested_error.ts.out create mode 100644 cli/tests/workers/worker_types.ts delete mode 100644 cli/tests/workers_round_robin_bench.ts delete mode 100644 cli/tests/workers_startup_bench.ts delete mode 100644 cli/tests/workers_test.ts (limited to 'cli/tests') diff --git a/cli/tests/073_worker_error.ts b/cli/tests/073_worker_error.ts deleted file mode 100644 index 736c4fde6..000000000 --- a/cli/tests/073_worker_error.ts +++ /dev/null @@ -1,5 +0,0 @@ -const worker = new Worker( - new URL("subdir/worker_error.ts", import.meta.url).href, - { type: "module", name: "bar" }, -); -setTimeout(() => worker.terminate(), 30000); diff --git a/cli/tests/073_worker_error.ts.out b/cli/tests/073_worker_error.ts.out deleted file mode 100644 index e46478979..000000000 --- a/cli/tests/073_worker_error.ts.out +++ /dev/null @@ -1,7 +0,0 @@ -[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD] - at foo ([WILDCARD]) - at [WILDCARD] -error: Uncaught (in promise) Error: Unhandled error event reached main worker. - throw new Error("Unhandled error event reached main worker."); - ^ - at Worker.#poll ([WILDCARD]) diff --git a/cli/tests/074_worker_nested_error.ts b/cli/tests/074_worker_nested_error.ts deleted file mode 100644 index 975d897ca..000000000 --- a/cli/tests/074_worker_nested_error.ts +++ /dev/null @@ -1,5 +0,0 @@ -const worker = new Worker( - new URL("073_worker_error.ts", import.meta.url).href, - { type: "module", name: "baz" }, -); -setTimeout(() => worker.terminate(), 30000); diff --git a/cli/tests/074_worker_nested_error.ts.out b/cli/tests/074_worker_nested_error.ts.out deleted file mode 100644 index e46478979..000000000 --- a/cli/tests/074_worker_nested_error.ts.out +++ /dev/null @@ -1,7 +0,0 @@ -[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD] - at foo ([WILDCARD]) - at [WILDCARD] -error: Uncaught (in promise) Error: Unhandled error event reached main worker. - throw new Error("Unhandled error event reached main worker."); - ^ - at Worker.#poll ([WILDCARD]) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 7c0df57b7..15da2359d 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -2369,7 +2369,7 @@ console.log("finish"); .arg("--allow-net") .arg("--allow-read") .arg("--unstable") - .arg("workers_test.ts") + .arg("workers/test.ts") .spawn() .unwrap() .wait() @@ -2377,6 +2377,18 @@ console.log("finish"); assert!(status.success()); } + itest!(worker_error { + args: "run -A workers/worker_error.ts", + output: "workers/worker_error.ts.out", + exit_code: 1, + }); + + itest!(worker_nested_error { + args: "run -A workers/worker_nested_error.ts", + output: "workers/worker_nested_error.ts.out", + exit_code: 1, + }); + #[test] fn compiler_api() { let status = util::deno_cmd() @@ -2626,18 +2638,6 @@ console.log("finish"); http_server: true, }); - itest!(_073_worker_error { - args: "run -A 073_worker_error.ts", - output: "073_worker_error.ts.out", - exit_code: 1, - }); - - itest!(_074_worker_nested_error { - args: "run -A 074_worker_nested_error.ts", - output: "074_worker_nested_error.ts.out", - exit_code: 1, - }); - itest!(_075_import_local_query_hash { args: "run 075_import_local_query_hash.ts", output: "075_import_local_query_hash.ts.out", diff --git a/cli/tests/subdir/worker_error.ts b/cli/tests/subdir/worker_error.ts deleted file mode 100644 index 495971090..000000000 --- a/cli/tests/subdir/worker_error.ts +++ /dev/null @@ -1,5 +0,0 @@ -function foo() { - throw new Error("foo"); -} - -foo(); diff --git a/cli/tests/subdir/worker_location.ts b/cli/tests/subdir/worker_location.ts deleted file mode 100644 index 89fa83036..000000000 --- a/cli/tests/subdir/worker_location.ts +++ /dev/null @@ -1,6 +0,0 @@ -onmessage = function (): void { - postMessage( - `${location.href}, ${location instanceof WorkerLocation}`, - ); - close(); -}; diff --git a/cli/tests/subdir/worker_types.ts b/cli/tests/subdir/worker_types.ts deleted file mode 100644 index b67a3b782..000000000 --- a/cli/tests/subdir/worker_types.ts +++ /dev/null @@ -1,4 +0,0 @@ -// deno-lint-ignore require-await -self.onmessage = async (_msg: MessageEvent) => { - self.postMessage("hello"); -}; diff --git a/cli/tests/unit/worker_types.ts b/cli/tests/unit/worker_types.ts index 687060172..d7dd87c54 100644 --- a/cli/tests/unit/worker_types.ts +++ b/cli/tests/unit/worker_types.ts @@ -5,7 +5,7 @@ unitTest( { perms: { read: true } }, function utimeSyncFileSuccess() { const w = new Worker( - new URL("../subdir/worker_types.ts", import.meta.url).href, + new URL("../workers/worker_types.ts", import.meta.url).href, { type: "module" }, ); assert(w); diff --git a/cli/tests/worker_post_undefined.ts b/cli/tests/worker_post_undefined.ts deleted file mode 100644 index 1b9b8d6ca..000000000 --- a/cli/tests/worker_post_undefined.ts +++ /dev/null @@ -1,5 +0,0 @@ -self.onmessage = (ev: MessageEvent) => { - console.log("received in worker", ev.data); - self.postMessage(undefined); - console.log("posted from worker"); -}; diff --git a/cli/tests/workers/bench_round_robin.ts b/cli/tests/workers/bench_round_robin.ts new file mode 100644 index 000000000..7974760af --- /dev/null +++ b/cli/tests/workers/bench_round_robin.ts @@ -0,0 +1,65 @@ +// Benchmark measures time it takes to send a message to a group of workers one +// at a time and wait for a response from all of them. Just a general +// throughput and consistency benchmark. +const data = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n"; +const workerCount = 4; +const cmdsPerWorker = 400; + +import { Deferred, deferred } from "../../../test_util/std/async/deferred.ts"; + +function handleAsyncMsgFromWorker( + promiseTable: Map>, + msg: { cmdId: number; data: string }, +): void { + const promise = promiseTable.get(msg.cmdId); + if (promise === null) { + throw new Error(`Failed to find promise: cmdId: ${msg.cmdId}, msg: ${msg}`); + } + promise?.resolve(data); +} + +async function main(): Promise { + const workers: Array<[Map>, Worker]> = []; + for (let i = 1; i <= workerCount; ++i) { + const worker = new Worker( + new URL("bench_worker.ts", import.meta.url).href, + { type: "module" }, + ); + const promise = deferred(); + worker.onmessage = (e): void => { + if (e.data.cmdId === 0) promise.resolve(); + }; + worker.postMessage({ cmdId: 0, action: 2 }); + await promise; + workers.push([new Map(), worker]); + } + // assign callback function + for (const [promiseTable, worker] of workers) { + worker.onmessage = (e): void => { + handleAsyncMsgFromWorker(promiseTable, e.data); + }; + } + for (const cmdId of Array(cmdsPerWorker).keys()) { + const promises: Array> = []; + for (const [promiseTable, worker] of workers) { + const promise = deferred(); + promiseTable.set(cmdId, promise); + worker.postMessage({ cmdId: cmdId, action: 1, data }); + promises.push(promise); + } + for (const promise of promises) { + await promise; + } + } + for (const [, worker] of workers) { + const promise = deferred(); + worker.onmessage = (e): void => { + if (e.data.cmdId === 3) promise.resolve(); + }; + worker.postMessage({ action: 3 }); + await promise; + } + console.log("Finished!"); +} + +main(); diff --git a/cli/tests/workers/bench_startup.ts b/cli/tests/workers/bench_startup.ts new file mode 100644 index 000000000..5fbf23b45 --- /dev/null +++ b/cli/tests/workers/bench_startup.ts @@ -0,0 +1,33 @@ +// Benchmark measures time it takes to start and stop a number of workers. +const workerCount = 50; + +async function bench(): Promise { + const workers: Worker[] = []; + for (let i = 1; i <= workerCount; ++i) { + const worker = new Worker( + new URL("bench_worker.ts", import.meta.url).href, + { type: "module" }, + ); + const promise = new Promise((resolve): void => { + worker.onmessage = (e): void => { + if (e.data.cmdId === 0) resolve(); + }; + }); + worker.postMessage({ cmdId: 0, action: 2 }); + await promise; + workers.push(worker); + } + console.log("Done creating workers closing workers!"); + for (const worker of workers) { + const promise = new Promise((resolve): void => { + worker.onmessage = (e): void => { + if (e.data.cmdId === 3) resolve(); + }; + }); + worker.postMessage({ action: 3 }); + await promise; + } + console.log("Finished!"); +} + +bench(); diff --git a/cli/tests/workers/error.ts b/cli/tests/workers/error.ts new file mode 100644 index 000000000..495971090 --- /dev/null +++ b/cli/tests/workers/error.ts @@ -0,0 +1,5 @@ +function foo() { + throw new Error("foo"); +} + +foo(); diff --git a/cli/tests/workers/post_undefined.ts b/cli/tests/workers/post_undefined.ts new file mode 100644 index 000000000..1b9b8d6ca --- /dev/null +++ b/cli/tests/workers/post_undefined.ts @@ -0,0 +1,5 @@ +self.onmessage = (ev: MessageEvent) => { + console.log("received in worker", ev.data); + self.postMessage(undefined); + console.log("posted from worker"); +}; diff --git a/cli/tests/workers/test.ts b/cli/tests/workers/test.ts new file mode 100644 index 000000000..0888e01db --- /dev/null +++ b/cli/tests/workers/test.ts @@ -0,0 +1,677 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +// Requires to be run with `--allow-net` flag + +import { + assert, + assertEquals, + assertThrows, +} from "../../../test_util/std/testing/asserts.ts"; +import { deferred } from "../../../test_util/std/async/deferred.ts"; + +Deno.test({ + name: "worker terminate", + fn: async function (): Promise { + const promise = deferred(); + + const jsWorker = new Worker( + new URL("test_worker.js", import.meta.url).href, + { type: "module" }, + ); + const tsWorker = new Worker( + new URL("test_worker.ts", import.meta.url).href, + { type: "module", name: "tsWorker" }, + ); + + tsWorker.onmessage = (e): void => { + assertEquals(e.data, "Hello World"); + promise.resolve(); + }; + + jsWorker.onmessage = (e): void => { + assertEquals(e.data, "Hello World"); + tsWorker.postMessage("Hello World"); + }; + + jsWorker.onerror = (e: Event): void => { + e.preventDefault(); + jsWorker.postMessage("Hello World"); + }; + + jsWorker.postMessage("Hello World"); + await promise; + tsWorker.terminate(); + jsWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker from data url", + async fn() { + const promise = deferred(); + const tsWorker = new Worker( + "data:application/typescript;base64,aWYgKHNlbGYubmFtZSAhPT0gInRzV29ya2VyIikgewogIHRocm93IEVycm9yKGBJbnZhbGlkIHdvcmtlciBuYW1lOiAke3NlbGYubmFtZX0sIGV4cGVjdGVkIHRzV29ya2VyYCk7Cn0KCm9ubWVzc2FnZSA9IGZ1bmN0aW9uIChlKTogdm9pZCB7CiAgcG9zdE1lc3NhZ2UoZS5kYXRhKTsKICBjbG9zZSgpOwp9Owo=", + { type: "module", name: "tsWorker" }, + ); + + tsWorker.onmessage = (e): void => { + assertEquals(e.data, "Hello World"); + promise.resolve(); + }; + + tsWorker.postMessage("Hello World"); + + await promise; + tsWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker nested", + fn: async function (): Promise { + const promise = deferred(); + + const nestedWorker = new Worker( + new URL("nested_worker.js", import.meta.url).href, + { type: "module", name: "nested" }, + ); + + nestedWorker.onmessage = (e): void => { + assert(e.data.type !== "error"); + promise.resolve(); + }; + + nestedWorker.postMessage("Hello World"); + await promise; + nestedWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker throws when executing", + fn: async function (): Promise { + const promise = deferred(); + const throwingWorker = new Worker( + new URL("throwing_worker.js", import.meta.url).href, + { type: "module" }, + ); + + // deno-lint-ignore no-explicit-any + throwingWorker.onerror = (e: any): void => { + e.preventDefault(); + assert(/Uncaught Error: Thrown error/.test(e.message)); + promise.resolve(); + }; + + await promise; + throwingWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker globals", + fn: async function (): Promise { + const promise = deferred(); + const workerOptions: WorkerOptions = { type: "module" }; + const w = new Worker( + new URL("worker_globals.ts", import.meta.url).href, + workerOptions, + ); + w.onmessage = (e): void => { + assertEquals(e.data, "true, true, true"); + promise.resolve(); + }; + w.postMessage("Hello, world!"); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "worker fetch API", + fn: async function (): Promise { + const promise = deferred(); + + const fetchingWorker = new Worker( + new URL("fetching_worker.js", import.meta.url).href, + { type: "module" }, + ); + + // deno-lint-ignore no-explicit-any + fetchingWorker.onerror = (e: any): void => { + e.preventDefault(); + promise.reject(e.message); + }; + + // Defer promise.resolve() to allow worker to shut down + fetchingWorker.onmessage = (e): void => { + assert(e.data === "Done!"); + promise.resolve(); + }; + + await promise; + fetchingWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker terminate busy loop", + fn: async function (): Promise { + const promise = deferred(); + + const busyWorker = new Worker( + new URL("busy_worker.js", import.meta.url).href, + { type: "module" }, + ); + + let testResult = 0; + + busyWorker.onmessage = (e): void => { + testResult = e.data; + if (testResult >= 10000) { + busyWorker.terminate(); + busyWorker.onmessage = (_e): void => { + throw new Error("unreachable"); + }; + setTimeout(() => { + assertEquals(testResult, 10000); + promise.resolve(); + }, 100); + } + }; + + busyWorker.postMessage("ping"); + await promise; + }, +}); + +Deno.test({ + name: "worker race condition", + fn: async function (): Promise { + // See issue for details + // https://github.com/denoland/deno/issues/4080 + const promise = deferred(); + + const racyWorker = new Worker( + new URL("racy_worker.js", import.meta.url).href, + { type: "module" }, + ); + + racyWorker.onmessage = (e): void => { + assertEquals(e.data.buf.length, 999999); + racyWorker.onmessage = (_e): void => { + throw new Error("unreachable"); + }; + setTimeout(() => { + promise.resolve(); + }, 100); + }; + + await promise; + }, +}); + +Deno.test({ + name: "worker is event listener", + fn: async function (): Promise { + let messageHandlersCalled = 0; + let errorHandlersCalled = 0; + + const promise1 = deferred(); + const promise2 = deferred(); + + const worker = new Worker( + new URL("event_worker.js", import.meta.url).href, + { type: "module" }, + ); + + worker.onmessage = (_e: Event): void => { + messageHandlersCalled++; + }; + worker.addEventListener("message", (_e: Event) => { + messageHandlersCalled++; + }); + worker.addEventListener("message", (_e: Event) => { + messageHandlersCalled++; + promise1.resolve(); + }); + + worker.onerror = (e): void => { + errorHandlersCalled++; + e.preventDefault(); + }; + worker.addEventListener("error", (_e: Event) => { + errorHandlersCalled++; + }); + worker.addEventListener("error", (_e: Event) => { + errorHandlersCalled++; + promise2.resolve(); + }); + + worker.postMessage("ping"); + await promise1; + assertEquals(messageHandlersCalled, 3); + + worker.postMessage("boom"); + await promise2; + assertEquals(errorHandlersCalled, 3); + worker.terminate(); + }, +}); + +Deno.test({ + name: "worker scope is event listener", + fn: async function (): Promise { + const promise1 = deferred(); + + const worker = new Worker( + new URL("event_worker_scope.js", import.meta.url).href, + { type: "module" }, + ); + + worker.onmessage = (e: MessageEvent): void => { + const { messageHandlersCalled, errorHandlersCalled } = e.data; + assertEquals(messageHandlersCalled, 4); + assertEquals(errorHandlersCalled, 4); + promise1.resolve(); + }; + + worker.onerror = (_e): void => { + throw new Error("unreachable"); + }; + + worker.postMessage("boom"); + worker.postMessage("ping"); + await promise1; + worker.terminate(); + }, +}); + +Deno.test({ + name: "worker with Deno namespace", + fn: async function (): Promise { + const promise = deferred(); + const promise2 = deferred(); + + const regularWorker = new Worker( + new URL("non_deno_worker.js", import.meta.url).href, + { type: "module" }, + ); + const denoWorker = new Worker( + new URL("deno_worker.ts", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: "inherit", + }, + }, + ); + + regularWorker.onmessage = (e): void => { + assertEquals(e.data, "Hello World"); + regularWorker.terminate(); + promise.resolve(); + }; + + denoWorker.onmessage = (e): void => { + assertEquals(e.data, "Hello World"); + denoWorker.terminate(); + promise2.resolve(); + }; + + regularWorker.postMessage("Hello World"); + await promise; + denoWorker.postMessage("Hello World"); + await promise2; + }, +}); + +Deno.test({ + name: "worker with crypto in scope", + fn: async function (): Promise { + const promise = deferred(); + const w = new Worker( + new URL("worker_crypto.js", import.meta.url).href, + { type: "module" }, + ); + w.onmessage = (e): void => { + assertEquals(e.data, true); + promise.resolve(); + }; + w.postMessage(null); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "Worker event handler order", + fn: async function (): Promise { + const promise = deferred(); + const w = new Worker( + new URL("test_worker.ts", import.meta.url).href, + { type: "module", name: "tsWorker" }, + ); + const arr: number[] = []; + w.addEventListener("message", () => arr.push(1)); + w.onmessage = (e): void => { + arr.push(2); + }; + w.addEventListener("message", () => arr.push(3)); + w.addEventListener("message", () => { + assertEquals(arr, [1, 2, 3]); + promise.resolve(); + }); + w.postMessage("Hello World"); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "Worker immediate close", + fn: async function (): Promise { + const promise = deferred(); + const w = new Worker( + new URL("./immediately_close_worker.js", import.meta.url).href, + { type: "module" }, + ); + setTimeout(() => { + promise.resolve(); + }, 1000); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "Worker post undefined", + fn: async function (): Promise { + const promise = deferred(); + const worker = new Worker( + new URL("./post_undefined.ts", import.meta.url).href, + { type: "module" }, + ); + + const handleWorkerMessage = (e: MessageEvent): void => { + console.log("main <- worker:", e.data); + worker.terminate(); + promise.resolve(); + }; + + worker.addEventListener("messageerror", () => console.log("message error")); + worker.addEventListener("error", () => console.log("error")); + worker.addEventListener("message", handleWorkerMessage); + + console.log("\npost from parent"); + worker.postMessage(undefined); + await promise; + }, +}); + +Deno.test("Worker inherits permissions", async function () { + const promise = deferred(); + const worker = new Worker( + new URL("./read_check_worker.js", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: "inherit", + }, + }, + ); + + worker.onmessage = ({ data: hasPermission }) => { + assert(hasPermission); + promise.resolve(); + }; + + worker.postMessage(null); + + await promise; + worker.terminate(); +}); + +Deno.test("Worker limit children permissions", async function () { + const promise = deferred(); + const worker = new Worker( + new URL("./read_check_worker.js", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: { + read: false, + }, + }, + }, + ); + + worker.onmessage = ({ data: hasPermission }) => { + assert(!hasPermission); + promise.resolve(); + }; + + worker.postMessage(null); + + await promise; + worker.terminate(); +}); + +Deno.test("Worker limit children permissions granularly", async function () { + const promise = deferred(); + const worker = new Worker( + new URL("./read_check_granular_worker.js", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: { + read: [ + new URL("./read_check_worker.js", import.meta.url), + ], + }, + }, + }, + ); + + //Routes are relative to the spawned worker location + const routes = [ + { permission: false, route: "read_check_granular_worker.js" }, + { permission: true, route: "read_check_worker.js" }, + ]; + + let checked = 0; + worker.onmessage = ({ data }) => { + checked++; + assertEquals(data.hasPermission, routes[data.index].permission); + routes.shift(); + if (checked === routes.length) { + promise.resolve(); + } + }; + + routes.forEach(({ route }, index) => + worker.postMessage({ + index, + route, + }) + ); + + await promise; + worker.terminate(); +}); + +Deno.test("Nested worker limit children permissions", async function () { + const promise = deferred(); + + /** This worker has read permissions but doesn't grant them to its children */ + const worker = new Worker( + new URL("./parent_read_check_worker.js", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: "inherit", + }, + }, + ); + + worker.onmessage = ({ data }) => { + assert(data.parentHasPermission); + assert(!data.childHasPermission); + promise.resolve(); + }; + + worker.postMessage(null); + + await promise; + worker.terminate(); +}); + +Deno.test("Nested worker limit children permissions granularly", async function () { + const promise = deferred(); + + /** This worker has read permissions but doesn't grant them to its children */ + const worker = new Worker( + new URL("./parent_read_check_granular_worker.js", import.meta.url) + .href, + { + type: "module", + deno: { + namespace: true, + permissions: { + read: [ + new URL("./read_check_granular_worker.js", import.meta.url), + ], + }, + }, + }, + ); + + //Routes are relative to the spawned worker location + const routes = [ + { + childHasPermission: false, + parentHasPermission: true, + route: "read_check_granular_worker.js", + }, + { + childHasPermission: false, + parentHasPermission: false, + route: "read_check_worker.js", + }, + ]; + + let checked = 0; + worker.onmessage = ({ data }) => { + checked++; + assertEquals( + data.childHasPermission, + routes[data.index].childHasPermission, + ); + assertEquals( + data.parentHasPermission, + routes[data.index].parentHasPermission, + ); + if (checked === routes.length) { + promise.resolve(); + } + }; + + // Index needed cause requests will be handled asynchronously + routes.forEach(({ route }, index) => + worker.postMessage({ + index, + route, + }) + ); + + await promise; + worker.terminate(); +}); + +// This test relies on env permissions not being granted on main thread +Deno.test("Worker initialization throws on worker permissions greater than parent thread permissions", function () { + assertThrows( + () => { + const worker = new Worker( + new URL("./deno_worker.ts", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: { + env: true, + }, + }, + }, + ); + worker.terminate(); + }, + Deno.errors.PermissionDenied, + "Can't escalate parent thread permissions", + ); +}); + +Deno.test("Worker with disabled permissions", async function () { + const promise = deferred(); + + const worker = new Worker( + new URL("./no_permissions_worker.js", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: "none", + }, + }, + ); + + worker.onmessage = ({ data: sandboxed }) => { + assert(sandboxed); + promise.resolve(); + }; + + worker.postMessage(null); + await promise; + worker.terminate(); +}); + +Deno.test({ + name: "worker location", + fn: async function (): Promise { + const promise = deferred(); + const workerModuleHref = + new URL("worker_location.ts", import.meta.url).href; + const w = new Worker(workerModuleHref, { type: "module" }); + w.onmessage = (e): void => { + assertEquals(e.data, `${workerModuleHref}, true`); + promise.resolve(); + }; + w.postMessage("Hello, world!"); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "worker with relative specifier", + fn: async function (): Promise { + assertEquals(location.href, "http://127.0.0.1:4545/cli/tests/"); + const promise = deferred(); + const w = new Worker( + "./workers/test_worker.ts", + { type: "module", name: "tsWorker" }, + ); + w.onmessage = (e): void => { + assertEquals(e.data, "Hello, world!"); + promise.resolve(); + }; + w.postMessage("Hello, world!"); + await promise; + w.terminate(); + }, +}); diff --git a/cli/tests/workers/worker_error.ts b/cli/tests/workers/worker_error.ts new file mode 100644 index 000000000..696680de8 --- /dev/null +++ b/cli/tests/workers/worker_error.ts @@ -0,0 +1,5 @@ +const worker = new Worker( + new URL("error.ts", import.meta.url).href, + { type: "module", name: "bar" }, +); +setTimeout(() => worker.terminate(), 30000); diff --git a/cli/tests/workers/worker_error.ts.out b/cli/tests/workers/worker_error.ts.out new file mode 100644 index 000000000..e46478979 --- /dev/null +++ b/cli/tests/workers/worker_error.ts.out @@ -0,0 +1,7 @@ +[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD] + at foo ([WILDCARD]) + at [WILDCARD] +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + throw new Error("Unhandled error event reached main worker."); + ^ + at Worker.#poll ([WILDCARD]) diff --git a/cli/tests/workers/worker_location.ts b/cli/tests/workers/worker_location.ts new file mode 100644 index 000000000..89fa83036 --- /dev/null +++ b/cli/tests/workers/worker_location.ts @@ -0,0 +1,6 @@ +onmessage = function (): void { + postMessage( + `${location.href}, ${location instanceof WorkerLocation}`, + ); + close(); +}; diff --git a/cli/tests/workers/worker_nested_error.ts b/cli/tests/workers/worker_nested_error.ts new file mode 100644 index 000000000..aba2011be --- /dev/null +++ b/cli/tests/workers/worker_nested_error.ts @@ -0,0 +1,5 @@ +const worker = new Worker( + new URL("worker_error.ts", import.meta.url).href, + { type: "module", name: "baz" }, +); +setTimeout(() => worker.terminate(), 30000); diff --git a/cli/tests/workers/worker_nested_error.ts.out b/cli/tests/workers/worker_nested_error.ts.out new file mode 100644 index 000000000..e46478979 --- /dev/null +++ b/cli/tests/workers/worker_nested_error.ts.out @@ -0,0 +1,7 @@ +[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD] + at foo ([WILDCARD]) + at [WILDCARD] +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + throw new Error("Unhandled error event reached main worker."); + ^ + at Worker.#poll ([WILDCARD]) diff --git a/cli/tests/workers/worker_types.ts b/cli/tests/workers/worker_types.ts new file mode 100644 index 000000000..b67a3b782 --- /dev/null +++ b/cli/tests/workers/worker_types.ts @@ -0,0 +1,4 @@ +// deno-lint-ignore require-await +self.onmessage = async (_msg: MessageEvent) => { + self.postMessage("hello"); +}; diff --git a/cli/tests/workers_round_robin_bench.ts b/cli/tests/workers_round_robin_bench.ts deleted file mode 100644 index 3dee290fe..000000000 --- a/cli/tests/workers_round_robin_bench.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Benchmark measures time it takes to send a message to a group of workers one -// at a time and wait for a response from all of them. Just a general -// throughput and consistency benchmark. -const data = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n"; -const workerCount = 4; -const cmdsPerWorker = 400; - -import { Deferred, deferred } from "../../test_util/std/async/deferred.ts"; - -function handleAsyncMsgFromWorker( - promiseTable: Map>, - msg: { cmdId: number; data: string }, -): void { - const promise = promiseTable.get(msg.cmdId); - if (promise === null) { - throw new Error(`Failed to find promise: cmdId: ${msg.cmdId}, msg: ${msg}`); - } - promise?.resolve(data); -} - -async function main(): Promise { - const workers: Array<[Map>, Worker]> = []; - for (let i = 1; i <= workerCount; ++i) { - const worker = new Worker( - new URL("workers/bench_worker.ts", import.meta.url).href, - { type: "module" }, - ); - const promise = deferred(); - worker.onmessage = (e): void => { - if (e.data.cmdId === 0) promise.resolve(); - }; - worker.postMessage({ cmdId: 0, action: 2 }); - await promise; - workers.push([new Map(), worker]); - } - // assign callback function - for (const [promiseTable, worker] of workers) { - worker.onmessage = (e): void => { - handleAsyncMsgFromWorker(promiseTable, e.data); - }; - } - for (const cmdId of Array(cmdsPerWorker).keys()) { - const promises: Array> = []; - for (const [promiseTable, worker] of workers) { - const promise = deferred(); - promiseTable.set(cmdId, promise); - worker.postMessage({ cmdId: cmdId, action: 1, data }); - promises.push(promise); - } - for (const promise of promises) { - await promise; - } - } - for (const [, worker] of workers) { - const promise = deferred(); - worker.onmessage = (e): void => { - if (e.data.cmdId === 3) promise.resolve(); - }; - worker.postMessage({ action: 3 }); - await promise; - } - console.log("Finished!"); -} - -main(); diff --git a/cli/tests/workers_startup_bench.ts b/cli/tests/workers_startup_bench.ts deleted file mode 100644 index f85bd13a1..000000000 --- a/cli/tests/workers_startup_bench.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Benchmark measures time it takes to start and stop a number of workers. -const workerCount = 50; - -async function bench(): Promise { - const workers: Worker[] = []; - for (let i = 1; i <= workerCount; ++i) { - const worker = new Worker( - new URL("workers/bench_worker.ts", import.meta.url).href, - { type: "module" }, - ); - const promise = new Promise((resolve): void => { - worker.onmessage = (e): void => { - if (e.data.cmdId === 0) resolve(); - }; - }); - worker.postMessage({ cmdId: 0, action: 2 }); - await promise; - workers.push(worker); - } - console.log("Done creating workers closing workers!"); - for (const worker of workers) { - const promise = new Promise((resolve): void => { - worker.onmessage = (e): void => { - if (e.data.cmdId === 3) resolve(); - }; - }); - worker.postMessage({ action: 3 }); - await promise; - } - console.log("Finished!"); -} - -bench(); diff --git a/cli/tests/workers_test.ts b/cli/tests/workers_test.ts deleted file mode 100644 index 383cad8a0..000000000 --- a/cli/tests/workers_test.ts +++ /dev/null @@ -1,678 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -// Requires to be run with `--allow-net` flag - -import { - assert, - assertEquals, - assertThrows, - fail, -} from "../../test_util/std/testing/asserts.ts"; -import { deferred } from "../../test_util/std/async/deferred.ts"; - -Deno.test({ - name: "worker terminate", - fn: async function (): Promise { - const promise = deferred(); - - const jsWorker = new Worker( - new URL("workers/test_worker.js", import.meta.url).href, - { type: "module" }, - ); - const tsWorker = new Worker( - new URL("workers/test_worker.ts", import.meta.url).href, - { type: "module", name: "tsWorker" }, - ); - - tsWorker.onmessage = (e): void => { - assertEquals(e.data, "Hello World"); - promise.resolve(); - }; - - jsWorker.onmessage = (e): void => { - assertEquals(e.data, "Hello World"); - tsWorker.postMessage("Hello World"); - }; - - jsWorker.onerror = (e: Event): void => { - e.preventDefault(); - jsWorker.postMessage("Hello World"); - }; - - jsWorker.postMessage("Hello World"); - await promise; - tsWorker.terminate(); - jsWorker.terminate(); - }, -}); - -Deno.test({ - name: "worker from data url", - async fn() { - const promise = deferred(); - const tsWorker = new Worker( - "data:application/typescript;base64,aWYgKHNlbGYubmFtZSAhPT0gInRzV29ya2VyIikgewogIHRocm93IEVycm9yKGBJbnZhbGlkIHdvcmtlciBuYW1lOiAke3NlbGYubmFtZX0sIGV4cGVjdGVkIHRzV29ya2VyYCk7Cn0KCm9ubWVzc2FnZSA9IGZ1bmN0aW9uIChlKTogdm9pZCB7CiAgcG9zdE1lc3NhZ2UoZS5kYXRhKTsKICBjbG9zZSgpOwp9Owo=", - { type: "module", name: "tsWorker" }, - ); - - tsWorker.onmessage = (e): void => { - assertEquals(e.data, "Hello World"); - promise.resolve(); - }; - - tsWorker.postMessage("Hello World"); - - await promise; - tsWorker.terminate(); - }, -}); - -Deno.test({ - name: "worker nested", - fn: async function (): Promise { - const promise = deferred(); - - const nestedWorker = new Worker( - new URL("workers/nested_worker.js", import.meta.url).href, - { type: "module", name: "nested" }, - ); - - nestedWorker.onmessage = (e): void => { - assert(e.data.type !== "error"); - promise.resolve(); - }; - - nestedWorker.postMessage("Hello World"); - await promise; - nestedWorker.terminate(); - }, -}); - -Deno.test({ - name: "worker throws when executing", - fn: async function (): Promise { - const promise = deferred(); - const throwingWorker = new Worker( - new URL("workers/throwing_worker.js", import.meta.url).href, - { type: "module" }, - ); - - // deno-lint-ignore no-explicit-any - throwingWorker.onerror = (e: any): void => { - e.preventDefault(); - assert(/Uncaught Error: Thrown error/.test(e.message)); - promise.resolve(); - }; - - await promise; - throwingWorker.terminate(); - }, -}); - -Deno.test({ - name: "worker globals", - fn: async function (): Promise { - const promise = deferred(); - const workerOptions: WorkerOptions = { type: "module" }; - const w = new Worker( - new URL("workers/worker_globals.ts", import.meta.url).href, - workerOptions, - ); - w.onmessage = (e): void => { - assertEquals(e.data, "true, true, true"); - promise.resolve(); - }; - w.postMessage("Hello, world!"); - await promise; - w.terminate(); - }, -}); - -Deno.test({ - name: "worker fetch API", - fn: async function (): Promise { - const promise = deferred(); - - const fetchingWorker = new Worker( - new URL("workers/fetching_worker.js", import.meta.url).href, - { type: "module" }, - ); - - // deno-lint-ignore no-explicit-any - fetchingWorker.onerror = (e: any): void => { - e.preventDefault(); - promise.reject(e.message); - }; - - // Defer promise.resolve() to allow worker to shut down - fetchingWorker.onmessage = (e): void => { - assert(e.data === "Done!"); - promise.resolve(); - }; - - await promise; - fetchingWorker.terminate(); - }, -}); - -Deno.test({ - name: "worker terminate busy loop", - fn: async function (): Promise { - const promise = deferred(); - - const busyWorker = new Worker( - new URL("workers/busy_worker.js", import.meta.url).href, - { type: "module" }, - ); - - let testResult = 0; - - busyWorker.onmessage = (e): void => { - testResult = e.data; - if (testResult >= 10000) { - busyWorker.terminate(); - busyWorker.onmessage = (_e): void => { - throw new Error("unreachable"); - }; - setTimeout(() => { - assertEquals(testResult, 10000); - promise.resolve(); - }, 100); - } - }; - - busyWorker.postMessage("ping"); - await promise; - }, -}); - -Deno.test({ - name: "worker race condition", - fn: async function (): Promise { - // See issue for details - // https://github.com/denoland/deno/issues/4080 - const promise = deferred(); - - const racyWorker = new Worker( - new URL("workers/racy_worker.js", import.meta.url).href, - { type: "module" }, - ); - - racyWorker.onmessage = (e): void => { - assertEquals(e.data.buf.length, 999999); - racyWorker.onmessage = (_e): void => { - throw new Error("unreachable"); - }; - setTimeout(() => { - promise.resolve(); - }, 100); - }; - - await promise; - }, -}); - -Deno.test({ - name: "worker is event listener", - fn: async function (): Promise { - let messageHandlersCalled = 0; - let errorHandlersCalled = 0; - - const promise1 = deferred(); - const promise2 = deferred(); - - const worker = new Worker( - new URL("workers/event_worker.js", import.meta.url).href, - { type: "module" }, - ); - - worker.onmessage = (_e: Event): void => { - messageHandlersCalled++; - }; - worker.addEventListener("message", (_e: Event) => { - messageHandlersCalled++; - }); - worker.addEventListener("message", (_e: Event) => { - messageHandlersCalled++; - promise1.resolve(); - }); - - worker.onerror = (e): void => { - errorHandlersCalled++; - e.preventDefault(); - }; - worker.addEventListener("error", (_e: Event) => { - errorHandlersCalled++; - }); - worker.addEventListener("error", (_e: Event) => { - errorHandlersCalled++; - promise2.resolve(); - }); - - worker.postMessage("ping"); - await promise1; - assertEquals(messageHandlersCalled, 3); - - worker.postMessage("boom"); - await promise2; - assertEquals(errorHandlersCalled, 3); - worker.terminate(); - }, -}); - -Deno.test({ - name: "worker scope is event listener", - fn: async function (): Promise { - const promise1 = deferred(); - - const worker = new Worker( - new URL("workers/event_worker_scope.js", import.meta.url).href, - { type: "module" }, - ); - - worker.onmessage = (e: MessageEvent): void => { - const { messageHandlersCalled, errorHandlersCalled } = e.data; - assertEquals(messageHandlersCalled, 4); - assertEquals(errorHandlersCalled, 4); - promise1.resolve(); - }; - - worker.onerror = (_e): void => { - throw new Error("unreachable"); - }; - - worker.postMessage("boom"); - worker.postMessage("ping"); - await promise1; - worker.terminate(); - }, -}); - -Deno.test({ - name: "worker with Deno namespace", - fn: async function (): Promise { - const promise = deferred(); - const promise2 = deferred(); - - const regularWorker = new Worker( - new URL("workers/non_deno_worker.js", import.meta.url).href, - { type: "module" }, - ); - const denoWorker = new Worker( - new URL("workers/deno_worker.ts", import.meta.url).href, - { - type: "module", - deno: { - namespace: true, - permissions: "inherit", - }, - }, - ); - - regularWorker.onmessage = (e): void => { - assertEquals(e.data, "Hello World"); - regularWorker.terminate(); - promise.resolve(); - }; - - denoWorker.onmessage = (e): void => { - assertEquals(e.data, "Hello World"); - denoWorker.terminate(); - promise2.resolve(); - }; - - regularWorker.postMessage("Hello World"); - await promise; - denoWorker.postMessage("Hello World"); - await promise2; - }, -}); - -Deno.test({ - name: "worker with crypto in scope", - fn: async function (): Promise { - const promise = deferred(); - const w = new Worker( - new URL("workers/worker_crypto.js", import.meta.url).href, - { type: "module" }, - ); - w.onmessage = (e): void => { - assertEquals(e.data, true); - promise.resolve(); - }; - w.postMessage(null); - await promise; - w.terminate(); - }, -}); - -Deno.test({ - name: "Worker event handler order", - fn: async function (): Promise { - const promise = deferred(); - const w = new Worker( - new URL("workers/test_worker.ts", import.meta.url).href, - { type: "module", name: "tsWorker" }, - ); - const arr: number[] = []; - w.addEventListener("message", () => arr.push(1)); - w.onmessage = (e): void => { - arr.push(2); - }; - w.addEventListener("message", () => arr.push(3)); - w.addEventListener("message", () => { - assertEquals(arr, [1, 2, 3]); - promise.resolve(); - }); - w.postMessage("Hello World"); - await promise; - w.terminate(); - }, -}); - -Deno.test({ - name: "Worker immediate close", - fn: async function (): Promise { - const promise = deferred(); - const w = new Worker( - new URL("./workers/immediately_close_worker.js", import.meta.url).href, - { type: "module" }, - ); - setTimeout(() => { - promise.resolve(); - }, 1000); - await promise; - w.terminate(); - }, -}); - -Deno.test({ - name: "Worker post undefined", - fn: async function (): Promise { - const promise = deferred(); - const worker = new Worker( - new URL("./worker_post_undefined.ts", import.meta.url).href, - { type: "module" }, - ); - - const handleWorkerMessage = (e: MessageEvent): void => { - console.log("main <- worker:", e.data); - worker.terminate(); - promise.resolve(); - }; - - worker.addEventListener("messageerror", () => console.log("message error")); - worker.addEventListener("error", () => console.log("error")); - worker.addEventListener("message", handleWorkerMessage); - - console.log("\npost from parent"); - worker.postMessage(undefined); - await promise; - }, -}); - -Deno.test("Worker inherits permissions", async function () { - const promise = deferred(); - const worker = new Worker( - new URL("./workers/read_check_worker.js", import.meta.url).href, - { - type: "module", - deno: { - namespace: true, - permissions: "inherit", - }, - }, - ); - - worker.onmessage = ({ data: hasPermission }) => { - assert(hasPermission); - promise.resolve(); - }; - - worker.postMessage(null); - - await promise; - worker.terminate(); -}); - -Deno.test("Worker limit children permissions", async function () { - const promise = deferred(); - const worker = new Worker( - new URL("./workers/read_check_worker.js", import.meta.url).href, - { - type: "module", - deno: { - namespace: true, - permissions: { - read: false, - }, - }, - }, - ); - - worker.onmessage = ({ data: hasPermission }) => { - assert(!hasPermission); - promise.resolve(); - }; - - worker.postMessage(null); - - await promise; - worker.terminate(); -}); - -Deno.test("Worker limit children permissions granularly", async function () { - const promise = deferred(); - const worker = new Worker( - new URL("./workers/read_check_granular_worker.js", import.meta.url).href, - { - type: "module", - deno: { - namespace: true, - permissions: { - read: [ - new URL("./workers/read_check_worker.js", import.meta.url), - ], - }, - }, - }, - ); - - //Routes are relative to the spawned worker location - const routes = [ - { permission: false, route: "read_check_granular_worker.js" }, - { permission: true, route: "read_check_worker.js" }, - ]; - - let checked = 0; - worker.onmessage = ({ data }) => { - checked++; - assertEquals(data.hasPermission, routes[data.index].permission); - routes.shift(); - if (checked === routes.length) { - promise.resolve(); - } - }; - - routes.forEach(({ route }, index) => - worker.postMessage({ - index, - route, - }) - ); - - await promise; - worker.terminate(); -}); - -Deno.test("Nested worker limit children permissions", async function () { - const promise = deferred(); - - /** This worker has read permissions but doesn't grant them to its children */ - const worker = new Worker( - new URL("./workers/parent_read_check_worker.js", import.meta.url).href, - { - type: "module", - deno: { - namespace: true, - permissions: "inherit", - }, - }, - ); - - worker.onmessage = ({ data }) => { - assert(data.parentHasPermission); - assert(!data.childHasPermission); - promise.resolve(); - }; - - worker.postMessage(null); - - await promise; - worker.terminate(); -}); - -Deno.test("Nested worker limit children permissions granularly", async function () { - const promise = deferred(); - - /** This worker has read permissions but doesn't grant them to its children */ - const worker = new Worker( - new URL("./workers/parent_read_check_granular_worker.js", import.meta.url) - .href, - { - type: "module", - deno: { - namespace: true, - permissions: { - read: [ - new URL("./workers/read_check_granular_worker.js", import.meta.url), - ], - }, - }, - }, - ); - - //Routes are relative to the spawned worker location - const routes = [ - { - childHasPermission: false, - parentHasPermission: true, - route: "read_check_granular_worker.js", - }, - { - childHasPermission: false, - parentHasPermission: false, - route: "read_check_worker.js", - }, - ]; - - let checked = 0; - worker.onmessage = ({ data }) => { - checked++; - assertEquals( - data.childHasPermission, - routes[data.index].childHasPermission, - ); - assertEquals( - data.parentHasPermission, - routes[data.index].parentHasPermission, - ); - if (checked === routes.length) { - promise.resolve(); - } - }; - - // Index needed cause requests will be handled asynchronously - routes.forEach(({ route }, index) => - worker.postMessage({ - index, - route, - }) - ); - - await promise; - worker.terminate(); -}); - -// This test relies on env permissions not being granted on main thread -Deno.test("Worker initialization throws on worker permissions greater than parent thread permissions", function () { - assertThrows( - () => { - const worker = new Worker( - new URL("./workers/deno_worker.ts", import.meta.url).href, - { - type: "module", - deno: { - namespace: true, - permissions: { - env: true, - }, - }, - }, - ); - worker.terminate(); - }, - Deno.errors.PermissionDenied, - "Can't escalate parent thread permissions", - ); -}); - -Deno.test("Worker with disabled permissions", async function () { - const promise = deferred(); - - const worker = new Worker( - new URL("./workers/no_permissions_worker.js", import.meta.url).href, - { - type: "module", - deno: { - namespace: true, - permissions: "none", - }, - }, - ); - - worker.onmessage = ({ data: sandboxed }) => { - assert(sandboxed); - promise.resolve(); - }; - - worker.postMessage(null); - await promise; - worker.terminate(); -}); - -Deno.test({ - name: "worker location", - fn: async function (): Promise { - const promise = deferred(); - const workerModuleHref = - new URL("subdir/worker_location.ts", import.meta.url).href; - const w = new Worker(workerModuleHref, { type: "module" }); - w.onmessage = (e): void => { - assertEquals(e.data, `${workerModuleHref}, true`); - promise.resolve(); - }; - w.postMessage("Hello, world!"); - await promise; - w.terminate(); - }, -}); - -Deno.test({ - name: "worker with relative specifier", - fn: async function (): Promise { - assertEquals(location.href, "http://127.0.0.1:4545/cli/tests/"); - const promise = deferred(); - const w = new Worker( - "./workers/test_worker.ts", - { type: "module", name: "tsWorker" }, - ); - w.onmessage = (e): void => { - assertEquals(e.data, "Hello, world!"); - promise.resolve(); - }; - w.postMessage("Hello, world!"); - await promise; - w.terminate(); - }, -}); -- cgit v1.2.3