diff options
Diffstat (limited to 'cli/tests/testdata/workers')
60 files changed, 1515 insertions, 0 deletions
diff --git a/cli/tests/testdata/workers/bench_large_message.ts b/cli/tests/testdata/workers/bench_large_message.ts new file mode 100644 index 000000000..a89ea9a78 --- /dev/null +++ b/cli/tests/testdata/workers/bench_large_message.ts @@ -0,0 +1,31 @@ +// Copyright 2020 the Deno authors. All rights reserved. MIT license. + +function oneWorker(i: number) { + return new Promise<void>((resolve) => { + let countDown = 10; + const worker = new Worker( + new URL("worker_large_message.js", import.meta.url).href, + { type: "module" }, + ); + worker.onmessage = (_e) => { + if (countDown > 0) { + countDown--; + return; + } + worker.terminate(); + resolve(); + }; + worker.postMessage("hi " + i); + }); +} + +function bench() { + const promises = []; + for (let i = 0; i < 50; i++) { + promises.push(oneWorker(i)); + } + + return Promise.all(promises); +} + +bench(); diff --git a/cli/tests/testdata/workers/bench_round_robin.ts b/cli/tests/testdata/workers/bench_round_robin.ts new file mode 100644 index 000000000..13afe286b --- /dev/null +++ b/cli/tests/testdata/workers/bench_round_robin.ts @@ -0,0 +1,68 @@ +// 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<number, Deferred<string>>, + msg: { cmdId: number; data: string }, +) { + 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() { + const workers: Array<[Map<number, Deferred<string>>, 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) => { + 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) => { + handleAsyncMsgFromWorker(promiseTable, e.data); + }; + } + for (const cmdId of Array(cmdsPerWorker).keys()) { + const promises: Array<Promise<string>> = []; + for (const [promiseTable, worker] of workers) { + const promise = deferred<string>(); + 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) => { + if (e.data.cmdId === 3) promise.resolve(); + }; + worker.postMessage({ action: 3 }); + await promise; + } + console.log("Finished!"); +} + +main(); diff --git a/cli/tests/testdata/workers/bench_startup.ts b/cli/tests/testdata/workers/bench_startup.ts new file mode 100644 index 000000000..bcf21ef44 --- /dev/null +++ b/cli/tests/testdata/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() { + 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<void>((resolve) => { + worker.onmessage = (e) => { + 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<void>((resolve) => { + worker.onmessage = (e) => { + if (e.data.cmdId === 3) resolve(); + }; + }); + worker.postMessage({ action: 3 }); + await promise; + } + console.log("Finished!"); +} + +bench(); diff --git a/cli/tests/testdata/workers/bench_worker.ts b/cli/tests/testdata/workers/bench_worker.ts new file mode 100644 index 000000000..1edd2750f --- /dev/null +++ b/cli/tests/testdata/workers/bench_worker.ts @@ -0,0 +1,21 @@ +onmessage = function (e) { + const { cmdId, action, data } = e.data; + switch (action) { + case 0: // Static response + postMessage({ + cmdId, + data: "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n", + }); + break; + case 1: // Respond with request data + postMessage({ cmdId, data }); + break; + case 2: // Ping + postMessage({ cmdId }); + break; + case 3: // Close + postMessage({ cmdId: 3 }); + close(); + break; + } +}; diff --git a/cli/tests/testdata/workers/broadcast_channel.ts b/cli/tests/testdata/workers/broadcast_channel.ts new file mode 100644 index 000000000..5076e9eb7 --- /dev/null +++ b/cli/tests/testdata/workers/broadcast_channel.ts @@ -0,0 +1,5 @@ +new BroadcastChannel("intercom").onmessage = function (e) { + this.postMessage(e.data); +}; + +self.postMessage("go"); diff --git a/cli/tests/testdata/workers/busy_worker.js b/cli/tests/testdata/workers/busy_worker.js new file mode 100644 index 000000000..7deba0321 --- /dev/null +++ b/cli/tests/testdata/workers/busy_worker.js @@ -0,0 +1,8 @@ +self.onmessage = function (_evt) { + // infinite loop + for (let i = 0; true; i++) { + if (i % 1000 == 0) { + postMessage(i); + } + } +}; diff --git a/cli/tests/testdata/workers/close_race_worker.js b/cli/tests/testdata/workers/close_race_worker.js new file mode 100644 index 000000000..f582a0d99 --- /dev/null +++ b/cli/tests/testdata/workers/close_race_worker.js @@ -0,0 +1,6 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +setTimeout(() => { + self.postMessage(""); + self.close(); +}, 500); diff --git a/cli/tests/testdata/workers/deno_worker.ts b/cli/tests/testdata/workers/deno_worker.ts new file mode 100644 index 000000000..2a29c8c4d --- /dev/null +++ b/cli/tests/testdata/workers/deno_worker.ts @@ -0,0 +1,7 @@ +onmessage = function (e) { + if (typeof self.Deno === "undefined") { + throw new Error("Deno namespace not available in worker"); + } + + postMessage(e.data); +}; diff --git a/cli/tests/testdata/workers/dynamic_remote.ts b/cli/tests/testdata/workers/dynamic_remote.ts new file mode 100644 index 000000000..381c7f374 --- /dev/null +++ b/cli/tests/testdata/workers/dynamic_remote.ts @@ -0,0 +1,2 @@ +// This file doesn't really exist, but it doesn't matter, a "PermissionsDenied" error should be thrown. +await import("https://example.com/some/file.ts"); diff --git a/cli/tests/testdata/workers/error.ts b/cli/tests/testdata/workers/error.ts new file mode 100644 index 000000000..495971090 --- /dev/null +++ b/cli/tests/testdata/workers/error.ts @@ -0,0 +1,5 @@ +function foo() { + throw new Error("foo"); +} + +foo(); diff --git a/cli/tests/testdata/workers/event_worker.js b/cli/tests/testdata/workers/event_worker.js new file mode 100644 index 000000000..849b6026c --- /dev/null +++ b/cli/tests/testdata/workers/event_worker.js @@ -0,0 +1,7 @@ +onmessage = function (e) { + if (e.data === "boom") { + throw new Error("boom error!"); + } + + postMessage(e.data); +}; diff --git a/cli/tests/testdata/workers/event_worker_scope.js b/cli/tests/testdata/workers/event_worker_scope.js new file mode 100644 index 000000000..0381801a8 --- /dev/null +++ b/cli/tests/testdata/workers/event_worker_scope.js @@ -0,0 +1,43 @@ +let messageHandlersCalled = 0; +let errorHandlersCalled = 0; + +onmessage = function (e) { + if (e.data === "boom") { + throw new Error("boom error!"); + } + messageHandlersCalled++; +}; + +self.addEventListener("message", (_e) => { + messageHandlersCalled++; +}); + +self.addEventListener("message", (_e) => { + messageHandlersCalled++; +}); + +self.addEventListener("message", (_e) => { + messageHandlersCalled++; + + postMessage({ + messageHandlersCalled, + errorHandlersCalled, + }); +}); + +onerror = function (_e) { + errorHandlersCalled++; +}; + +self.addEventListener("error", (_e) => { + errorHandlersCalled++; +}); + +self.addEventListener("error", (_e) => { + errorHandlersCalled++; +}); + +self.addEventListener("error", (e) => { + errorHandlersCalled++; + e.preventDefault(); +}); diff --git a/cli/tests/testdata/workers/fetching_worker.js b/cli/tests/testdata/workers/fetching_worker.js new file mode 100644 index 000000000..77ff471d7 --- /dev/null +++ b/cli/tests/testdata/workers/fetching_worker.js @@ -0,0 +1,6 @@ +const r = await fetch( + "http://localhost:4545/workers/fetching_worker.js", +); +await r.text(); +postMessage("Done!"); +close(); diff --git a/cli/tests/testdata/workers/http_worker.js b/cli/tests/testdata/workers/http_worker.js new file mode 100644 index 000000000..34603ed56 --- /dev/null +++ b/cli/tests/testdata/workers/http_worker.js @@ -0,0 +1,11 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +const listener = Deno.listen({ hostname: "127.0.0.1", port: 4506 }); +postMessage("ready"); +for await (const conn of listener) { + (async () => { + const requests = Deno.serveHttp(conn); + for await (const { respondWith } of requests) { + respondWith(new Response("Hello world")); + } + })(); +} diff --git a/cli/tests/testdata/workers/immediately_close_worker.js b/cli/tests/testdata/workers/immediately_close_worker.js new file mode 100644 index 000000000..8fd27343a --- /dev/null +++ b/cli/tests/testdata/workers/immediately_close_worker.js @@ -0,0 +1 @@ +self.close(); diff --git a/cli/tests/testdata/workers/message_port.ts b/cli/tests/testdata/workers/message_port.ts new file mode 100644 index 000000000..d78304a39 --- /dev/null +++ b/cli/tests/testdata/workers/message_port.ts @@ -0,0 +1,14 @@ +const channel = new MessageChannel(); + +channel.port2.onmessage = (e) => { + channel.port2.postMessage(e.data === "2"); + channel.port2.close(); +}; + +self.postMessage("1", [channel.port1]); + +self.onmessage = (e) => { + const port1 = e.ports[0]; + port1.postMessage(e.data === "3"); + port1.close(); +}; diff --git a/cli/tests/testdata/workers/nested_worker.js b/cli/tests/testdata/workers/nested_worker.js new file mode 100644 index 000000000..4b51b8763 --- /dev/null +++ b/cli/tests/testdata/workers/nested_worker.js @@ -0,0 +1,18 @@ +// Specifier should be resolved relative to current file +const jsWorker = new Worker( + new URL("sibling_worker.js", import.meta.url).href, + { type: "module", name: "sibling" }, +); + +jsWorker.onerror = (_e) => { + postMessage({ type: "error" }); +}; + +jsWorker.onmessage = (e) => { + postMessage({ type: "msg", text: e }); + close(); +}; + +onmessage = function (e) { + jsWorker.postMessage(e.data); +}; diff --git a/cli/tests/testdata/workers/no_permissions_worker.js b/cli/tests/testdata/workers/no_permissions_worker.js new file mode 100644 index 000000000..db0d911ac --- /dev/null +++ b/cli/tests/testdata/workers/no_permissions_worker.js @@ -0,0 +1,17 @@ +self.onmessage = async () => { + const hrtime = await Deno.permissions.query({ name: "hrtime" }); + const net = await Deno.permissions.query({ name: "net" }); + const ffi = await Deno.permissions.query({ name: "ffi" }); + const read = await Deno.permissions.query({ name: "read" }); + const run = await Deno.permissions.query({ name: "run" }); + const write = await Deno.permissions.query({ name: "write" }); + self.postMessage( + hrtime.state === "denied" && + net.state === "denied" && + ffi.state === "denied" && + read.state === "denied" && + run.state === "denied" && + write.state === "denied", + ); + self.close(); +}; diff --git a/cli/tests/testdata/workers/non_deno_worker.js b/cli/tests/testdata/workers/non_deno_worker.js new file mode 100644 index 000000000..773721560 --- /dev/null +++ b/cli/tests/testdata/workers/non_deno_worker.js @@ -0,0 +1,7 @@ +onmessage = function (e) { + if (typeof self.Deno !== "undefined") { + throw new Error("Deno namespace unexpectedly available in worker"); + } + + postMessage(e.data); +}; diff --git a/cli/tests/testdata/workers/nonexistent_worker.out b/cli/tests/testdata/workers/nonexistent_worker.out new file mode 100644 index 000000000..1651321bc --- /dev/null +++ b/cli/tests/testdata/workers/nonexistent_worker.out @@ -0,0 +1,3 @@ +[WILDCARD]error: Uncaught (in worker "") Cannot resolve module "file:///[WILDCARD]/workers/doesnt_exist.js". +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/nonexistent_worker.ts b/cli/tests/testdata/workers/nonexistent_worker.ts new file mode 100644 index 000000000..8ebe29114 --- /dev/null +++ b/cli/tests/testdata/workers/nonexistent_worker.ts @@ -0,0 +1,5 @@ +const w = new Worker(new URL("doesnt_exist.js", import.meta.url).href, { + type: "module", +}); + +w.postMessage("hello"); diff --git a/cli/tests/testdata/workers/parent_read_check_granular_worker.js b/cli/tests/testdata/workers/parent_read_check_granular_worker.js new file mode 100644 index 000000000..1391190cd --- /dev/null +++ b/cli/tests/testdata/workers/parent_read_check_granular_worker.js @@ -0,0 +1,41 @@ +const worker = new Worker( + new URL("./read_check_granular_worker.js", import.meta.url).href, + { + type: "module", + deno: { + namespace: true, + permissions: { + read: [], + }, + }, + }, +); + +let received = 0; +const messages = []; + +worker.onmessage = ({ data: childResponse }) => { + received++; + postMessage({ + childHasPermission: childResponse.hasPermission, + index: childResponse.index, + parentHasPermission: messages[childResponse.index], + }); + if (received === messages.length) { + worker.terminate(); + } +}; + +onmessage = async ({ data }) => { + const { state } = await Deno.permissions.query({ + name: "read", + path: data.path, + }); + + messages[data.index] = state === "granted"; + + worker.postMessage({ + index: data.index, + route: data.route, + }); +}; diff --git a/cli/tests/testdata/workers/parent_read_check_worker.js b/cli/tests/testdata/workers/parent_read_check_worker.js new file mode 100644 index 000000000..ec92cca3f --- /dev/null +++ b/cli/tests/testdata/workers/parent_read_check_worker.js @@ -0,0 +1,27 @@ +onmessage = async () => { + const { state } = await Deno.permissions.query({ + name: "read", + }); + + 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: childHasPermission }) => { + postMessage({ + parentHasPermission: state === "granted", + childHasPermission, + }); + close(); + }; + worker.postMessage(null); +}; diff --git a/cli/tests/testdata/workers/permissions_blob_local.ts b/cli/tests/testdata/workers/permissions_blob_local.ts new file mode 100644 index 000000000..52f630bd8 --- /dev/null +++ b/cli/tests/testdata/workers/permissions_blob_local.ts @@ -0,0 +1,6 @@ +// This file doesn't really exist, but it doesn't matter, a "PermissionsDenied" error should be thrown. +const code = `import "file:///${ + Deno.build.os == "windows" ? "C:/" : "" +}local_file.ts";`; +const blob = new Blob([code]); +new Worker(URL.createObjectURL(blob), { type: "module" }); diff --git a/cli/tests/testdata/workers/permissions_blob_local.ts.out b/cli/tests/testdata/workers/permissions_blob_local.ts.out new file mode 100644 index 000000000..0835777ec --- /dev/null +++ b/cli/tests/testdata/workers/permissions_blob_local.ts.out @@ -0,0 +1,4 @@ +error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag + at blob:null/[WILDCARD]:1:0 +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/permissions_blob_remote.ts b/cli/tests/testdata/workers/permissions_blob_remote.ts new file mode 100644 index 000000000..4808bc57b --- /dev/null +++ b/cli/tests/testdata/workers/permissions_blob_remote.ts @@ -0,0 +1,4 @@ +// This file doesn't really exist, but it doesn't matter, a "PermissionsDenied" error should be thrown. +const code = `import "https://example.com/some/file.ts";`; +const blob = new Blob([code]); +new Worker(URL.createObjectURL(blob), { type: "module" }); diff --git a/cli/tests/testdata/workers/permissions_blob_remote.ts.out b/cli/tests/testdata/workers/permissions_blob_remote.ts.out new file mode 100644 index 000000000..2d01458ca --- /dev/null +++ b/cli/tests/testdata/workers/permissions_blob_remote.ts.out @@ -0,0 +1,4 @@ +error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag + at blob:null/[WILDCARD]:1:0 +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/permissions_data_local.ts b/cli/tests/testdata/workers/permissions_data_local.ts new file mode 100644 index 000000000..cda80bed6 --- /dev/null +++ b/cli/tests/testdata/workers/permissions_data_local.ts @@ -0,0 +1,7 @@ +// This file doesn't really exist, but it doesn't matter, a "PermissionsDenied" error should be thrown. +const code = `import "file:///${ + Deno.build.os == "windows" ? "C:/" : "" +}local_file.ts";`; +new Worker(`data:application/javascript;base64,${btoa(code)}`, { + type: "module", +}); diff --git a/cli/tests/testdata/workers/permissions_data_local.ts.out b/cli/tests/testdata/workers/permissions_data_local.ts.out new file mode 100644 index 000000000..2a6be2b57 --- /dev/null +++ b/cli/tests/testdata/workers/permissions_data_local.ts.out @@ -0,0 +1,4 @@ +error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag + at data:application/javascript;base64,[WILDCARD]:1:0 +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/permissions_data_remote.ts b/cli/tests/testdata/workers/permissions_data_remote.ts new file mode 100644 index 000000000..b37bd661d --- /dev/null +++ b/cli/tests/testdata/workers/permissions_data_remote.ts @@ -0,0 +1,5 @@ +// This file doesn't really exist, but it doesn't matter, a "PermissionsDenied" error should be thrown. +const code = `import "https://example.com/some/file.ts";`; +new Worker(`data:application/javascript;base64,${btoa(code)}`, { + type: "module", +}); diff --git a/cli/tests/testdata/workers/permissions_data_remote.ts.out b/cli/tests/testdata/workers/permissions_data_remote.ts.out new file mode 100644 index 000000000..90677892a --- /dev/null +++ b/cli/tests/testdata/workers/permissions_data_remote.ts.out @@ -0,0 +1,4 @@ +error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag + at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:0 +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/permissions_dynamic_remote.ts b/cli/tests/testdata/workers/permissions_dynamic_remote.ts new file mode 100644 index 000000000..54a361bc0 --- /dev/null +++ b/cli/tests/testdata/workers/permissions_dynamic_remote.ts @@ -0,0 +1,11 @@ +new Worker( + "http://localhost:4545/workers/dynamic_remote.ts", + { + type: "module", + deno: { + permissions: { + net: false, + }, + }, + }, +); diff --git a/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out b/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out new file mode 100644 index 000000000..3c4523ce0 --- /dev/null +++ b/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out @@ -0,0 +1,6 @@ +error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag +await import("https://example.com/some/file.ts"); +^ + at async http://localhost:4545/workers/dynamic_remote.ts:2:1 +[WILDCARD]error: Uncaught (in promise) Error: Unhandled error event reached main worker. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/permissions_remote_remote.ts b/cli/tests/testdata/workers/permissions_remote_remote.ts new file mode 100644 index 000000000..4df2a8a5d --- /dev/null +++ b/cli/tests/testdata/workers/permissions_remote_remote.ts @@ -0,0 +1,3 @@ +new Worker("http://localhost:4545/workers/static_remote.ts", { + type: "module", +}); diff --git a/cli/tests/testdata/workers/permissions_remote_remote.ts.out b/cli/tests/testdata/workers/permissions_remote_remote.ts.out new file mode 100644 index 000000000..94a92c72d --- /dev/null +++ b/cli/tests/testdata/workers/permissions_remote_remote.ts.out @@ -0,0 +1,4 @@ +error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag + at http://localhost:4545/workers/static_remote.ts:2:0 +error: Uncaught (in promise) Error: Unhandled error event reached main worker. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/post_undefined.ts b/cli/tests/testdata/workers/post_undefined.ts new file mode 100644 index 000000000..1b9b8d6ca --- /dev/null +++ b/cli/tests/testdata/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/testdata/workers/racy_worker.js b/cli/tests/testdata/workers/racy_worker.js new file mode 100644 index 000000000..0f66c6278 --- /dev/null +++ b/cli/tests/testdata/workers/racy_worker.js @@ -0,0 +1,25 @@ +// See issue for details +// https://github.com/denoland/deno/issues/4080 +// +// After first received message, this worker schedules +// [assert(), close(), assert()] ops on the same turn of microtask queue +// All tasks after close should not make it + +onmessage = async function () { + let stage = 0; + await new Promise((_) => { + setTimeout(() => { + if (stage !== 0) throw "Unexpected stage"; + stage = 1; + }, 50); + setTimeout(() => { + if (stage !== 1) throw "Unexpected stage"; + stage = 2; + postMessage("DONE"); + close(); + }, 50); + setTimeout(() => { + throw "This should not be run"; + }, 50); + }); +}; diff --git a/cli/tests/testdata/workers/read_check_granular_worker.js b/cli/tests/testdata/workers/read_check_granular_worker.js new file mode 100644 index 000000000..25f2058b3 --- /dev/null +++ b/cli/tests/testdata/workers/read_check_granular_worker.js @@ -0,0 +1,11 @@ +onmessage = async ({ data }) => { + const { state } = await Deno.permissions.query({ + name: "read", + path: data.path, + }); + + postMessage({ + hasPermission: state === "granted", + index: data.index, + }); +}; diff --git a/cli/tests/testdata/workers/read_check_worker.js b/cli/tests/testdata/workers/read_check_worker.js new file mode 100644 index 000000000..2ad01bf5b --- /dev/null +++ b/cli/tests/testdata/workers/read_check_worker.js @@ -0,0 +1,7 @@ +onmessage = async () => { + const { state } = await Deno.permissions.query({ + name: "read", + }); + postMessage(state === "granted"); + close(); +}; diff --git a/cli/tests/testdata/workers/shared_array_buffer.ts b/cli/tests/testdata/workers/shared_array_buffer.ts new file mode 100644 index 000000000..4af95863a --- /dev/null +++ b/cli/tests/testdata/workers/shared_array_buffer.ts @@ -0,0 +1,9 @@ +self.postMessage("ready"); + +globalThis.addEventListener("message", (e) => { + const bytes1 = new Uint8Array(e.data[0]); + const bytes2 = new Uint8Array(e.data[1]); + bytes1[0] = 1; + bytes2[0] = 2; + self.postMessage("done"); +}); diff --git a/cli/tests/testdata/workers/sibling_worker.js b/cli/tests/testdata/workers/sibling_worker.js new file mode 100644 index 000000000..99707e5d6 --- /dev/null +++ b/cli/tests/testdata/workers/sibling_worker.js @@ -0,0 +1,4 @@ +onmessage = (e) => { + postMessage(e.data); + close(); +}; diff --git a/cli/tests/testdata/workers/static_remote.ts b/cli/tests/testdata/workers/static_remote.ts new file mode 100644 index 000000000..2d6e820fd --- /dev/null +++ b/cli/tests/testdata/workers/static_remote.ts @@ -0,0 +1,2 @@ +// This file doesn't really exist, but it doesn't matter, a "PermissionsDenied" error should be thrown. +import "https://example.com/some/file.ts"; diff --git a/cli/tests/testdata/workers/test.ts b/cli/tests/testdata/workers/test.ts new file mode 100644 index 000000000..9d3855fe1 --- /dev/null +++ b/cli/tests/testdata/workers/test.ts @@ -0,0 +1,852 @@ +// 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"; +import { fromFileUrl } from "../../../../test_util/std/path/mod.ts"; + +Deno.test({ + name: "worker terminate", + fn: async function () { + 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) => { + assertEquals(e.data, "Hello World"); + promise.resolve(); + }; + + jsWorker.onmessage = (e) => { + assertEquals(e.data, "Hello World"); + tsWorker.postMessage("Hello World"); + }; + + jsWorker.onerror = (e: Event) => { + 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) => { + assertEquals(e.data, "Hello World"); + promise.resolve(); + }; + + tsWorker.postMessage("Hello World"); + + await promise; + tsWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker nested", + fn: async function () { + const promise = deferred(); + + const nestedWorker = new Worker( + new URL("nested_worker.js", import.meta.url).href, + { type: "module", name: "nested" }, + ); + + nestedWorker.onmessage = (e) => { + 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 () { + 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) => { + e.preventDefault(); + assert(/Uncaught Error: Thrown error/.test(e.message)); + promise.resolve(); + }; + + await promise; + throwingWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker globals", + fn: async function () { + 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) => { + assertEquals(e.data, "true, true, true, true"); + promise.resolve(); + }; + w.postMessage("Hello, world!"); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "worker fetch API", + fn: async function () { + 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) => { + e.preventDefault(); + promise.reject(e.message); + }; + + // Defer promise.resolve() to allow worker to shut down + fetchingWorker.onmessage = (e) => { + assert(e.data === "Done!"); + promise.resolve(); + }; + + await promise; + fetchingWorker.terminate(); + }, +}); + +Deno.test({ + name: "worker terminate busy loop", + fn: async function () { + const promise = deferred(); + + const busyWorker = new Worker( + new URL("busy_worker.js", import.meta.url), + { type: "module" }, + ); + + let testResult = 0; + + busyWorker.onmessage = (e) => { + testResult = e.data; + if (testResult >= 10000) { + busyWorker.terminate(); + busyWorker.onmessage = (_e) => { + 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 () { + // 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), + { type: "module" }, + ); + + racyWorker.onmessage = (_e) => { + setTimeout(() => { + promise.resolve(); + }, 100); + }; + + racyWorker.postMessage("START"); + await promise; + }, +}); + +Deno.test({ + name: "worker is event listener", + fn: async function () { + let messageHandlersCalled = 0; + let errorHandlersCalled = 0; + + const promise1 = deferred(); + const promise2 = deferred(); + + const worker = new Worker( + new URL("event_worker.js", import.meta.url), + { type: "module" }, + ); + + worker.onmessage = (_e: Event) => { + messageHandlersCalled++; + }; + worker.addEventListener("message", (_e: Event) => { + messageHandlersCalled++; + }); + worker.addEventListener("message", (_e: Event) => { + messageHandlersCalled++; + promise1.resolve(); + }); + + worker.onerror = (e) => { + 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 () { + const promise1 = deferred(); + + const worker = new Worker( + new URL("event_worker_scope.js", import.meta.url), + { type: "module" }, + ); + + worker.onmessage = (e: MessageEvent) => { + const { messageHandlersCalled, errorHandlersCalled } = e.data; + assertEquals(messageHandlersCalled, 4); + assertEquals(errorHandlersCalled, 4); + promise1.resolve(); + }; + + worker.onerror = (_e) => { + throw new Error("unreachable"); + }; + + worker.postMessage("boom"); + worker.postMessage("ping"); + await promise1; + worker.terminate(); + }, +}); + +Deno.test({ + name: "worker with Deno namespace", + fn: async function () { + const promise = deferred(); + const promise2 = deferred(); + + const regularWorker = new Worker( + new URL("non_deno_worker.js", import.meta.url), + { type: "module" }, + ); + const denoWorker = new Worker( + new URL("deno_worker.ts", import.meta.url), + { + type: "module", + deno: { + namespace: true, + permissions: "inherit", + }, + }, + ); + + regularWorker.onmessage = (e) => { + assertEquals(e.data, "Hello World"); + regularWorker.terminate(); + promise.resolve(); + }; + + denoWorker.onmessage = (e) => { + 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 () { + const promise = deferred(); + const w = new Worker( + new URL("worker_crypto.js", import.meta.url).href, + { type: "module" }, + ); + w.onmessage = (e) => { + assertEquals(e.data, true); + promise.resolve(); + }; + w.postMessage(null); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "Worker event handler order", + fn: async function () { + 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) => { + 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 () { + 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 () { + const promise = deferred(); + const worker = new Worker( + new URL("./post_undefined.ts", import.meta.url).href, + { type: "module" }, + ); + + const handleWorkerMessage = (e: MessageEvent) => { + 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, + path: fromFileUrl( + new URL("read_check_granular_worker.js", import.meta.url), + ), + }, + { + permission: true, + path: fromFileUrl(new URL("read_check_worker.js", import.meta.url)), + }, + ]; + + let checked = 0; + worker.onmessage = ({ data }) => { + checked++; + assertEquals(data.hasPermission, routes[data.index].permission); + routes.shift(); + if (checked === routes.length) { + promise.resolve(); + } + }; + + routes.forEach(({ path }, index) => + worker.postMessage({ + index, + path, + }) + ); + + 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, + path: fromFileUrl( + new URL("read_check_granular_worker.js", import.meta.url), + ), + }, + { + childHasPermission: false, + parentHasPermission: false, + path: fromFileUrl(new URL("read_check_worker.js", import.meta.url)), + }, + ]; + + 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(({ path }, index) => + worker.postMessage({ + index, + path, + }) + ); + + 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 () { + const promise = deferred(); + const workerModuleHref = + new URL("worker_location.ts", import.meta.url).href; + const w = new Worker(workerModuleHref, { type: "module" }); + w.onmessage = (e) => { + 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 () { + assertEquals(location.href, "http://127.0.0.1:4545/"); + const promise = deferred(); + const w = new Worker( + "./workers/test_worker.ts", + { type: "module", name: "tsWorker" }, + ); + w.onmessage = (e) => { + assertEquals(e.data, "Hello, world!"); + promise.resolve(); + }; + w.postMessage("Hello, world!"); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "Worker with top-level-await", + fn: async function () { + const result = deferred(); + const worker = new Worker( + new URL("worker_with_top_level_await.ts", import.meta.url).href, + { type: "module" }, + ); + worker.onmessage = (e) => { + if (e.data == "ready") { + worker.postMessage("trigger worker handler"); + } else if (e.data == "triggered worker handler") { + result.resolve(); + } else { + result.reject(new Error("Handler didn't run during top-level delay.")); + } + }; + await result; + worker.terminate(); + }, +}); + +Deno.test({ + name: "Worker with native HTTP", + fn: async function () { + const result = deferred(); + const worker = new Worker( + new URL( + "./http_worker.js", + import.meta.url, + ).href, + { + type: "module", + deno: { + namespace: true, + permissions: "inherit", + }, + }, + ); + worker.onmessage = () => { + result.resolve(); + }; + await result; + + assert(worker); + const response = await fetch("http://localhost:4506"); + assert(await response.arrayBuffer()); + worker.terminate(); + }, +}); + +Deno.test({ + name: "structured cloning postMessage", + fn: async function () { + const result = deferred(); + const worker = new Worker( + new URL("worker_structured_cloning.ts", import.meta.url).href, + { type: "module" }, + ); + + worker.onmessage = (e) => { + // self field should reference itself (circular ref) + const value = e.data.self.self.self; + + // fields a and b refer to the same array + assertEquals(value.a, ["a", true, 432]); + assertEquals(value.a, ["a", true, 432]); + value.b[0] = "b"; + value.a[2] += 5; + assertEquals(value.a, ["b", true, 437]); + assertEquals(value.b, ["b", true, 437]); + + const len = value.c.size; + value.c.add(1); // This value is already in the set. + value.c.add(2); + assertEquals(len + 1, value.c.size); + + result.resolve(); + }; + + worker.postMessage("START"); + await result; + worker.terminate(); + }, +}); + +Deno.test({ + name: "worker with relative specifier", + fn: async function () { + assertEquals(location.href, "http://127.0.0.1:4545/"); + const promise = deferred(); + const w = new Worker( + "./workers/test_worker.ts", + { type: "module", name: "tsWorker" }, + ); + w.onmessage = (e) => { + assertEquals(e.data, "Hello, world!"); + promise.resolve(); + }; + w.postMessage("Hello, world!"); + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "worker SharedArrayBuffer", + fn: async function () { + const promise = deferred(); + const workerOptions: WorkerOptions = { type: "module" }; + const w = new Worker( + new URL("shared_array_buffer.ts", import.meta.url).href, + workerOptions, + ); + const sab1 = new SharedArrayBuffer(1); + const sab2 = new SharedArrayBuffer(1); + const bytes1 = new Uint8Array(sab1); + const bytes2 = new Uint8Array(sab2); + assertEquals(bytes1[0], 0); + assertEquals(bytes2[0], 0); + w.onmessage = () => { + w.postMessage([sab1, sab2]); + w.onmessage = () => { + assertEquals(bytes1[0], 1); + assertEquals(bytes2[0], 2); + promise.resolve(); + }; + }; + await promise; + w.terminate(); + }, +}); + +Deno.test({ + name: "Send MessagePorts from / to workers", + fn: async function () { + const result = deferred(); + const worker = new Worker( + new URL("message_port.ts", import.meta.url).href, + { type: "module" }, + ); + + const channel = new MessageChannel(); + + worker.onmessage = (e) => { + assertEquals(e.data, "1"); + assertEquals(e.ports.length, 1); + const port1 = e.ports[0]; + port1.onmessage = (e) => { + assertEquals(e.data, true); + port1.close(); + worker.postMessage("3", [channel.port1]); + }; + port1.postMessage("2"); + }; + + channel.port2.onmessage = (e) => { + assertEquals(e.data, true); + channel.port2.close(); + result.resolve(); + }; + + await result; + worker.terminate(); + }, +}); diff --git a/cli/tests/testdata/workers/test.ts.out b/cli/tests/testdata/workers/test.ts.out new file mode 100644 index 000000000..1b4238a9b --- /dev/null +++ b/cli/tests/testdata/workers/test.ts.out @@ -0,0 +1,3 @@ +[WILDCARD] +test result: ok. [WILDCARD] passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD]ms) + diff --git a/cli/tests/testdata/workers/test_worker.js b/cli/tests/testdata/workers/test_worker.js new file mode 100644 index 000000000..4260975a6 --- /dev/null +++ b/cli/tests/testdata/workers/test_worker.js @@ -0,0 +1,19 @@ +let thrown = false; + +if (self.name !== "") { + throw Error(`Bad worker name: ${self.name}, expected empty string.`); +} + +onmessage = function (e) { + if (thrown === false) { + thrown = true; + throw new SyntaxError("[test error]"); + } + + postMessage(e.data); + close(); +}; + +onerror = function () { + return false; +}; diff --git a/cli/tests/testdata/workers/test_worker.ts b/cli/tests/testdata/workers/test_worker.ts new file mode 100644 index 000000000..996476058 --- /dev/null +++ b/cli/tests/testdata/workers/test_worker.ts @@ -0,0 +1,8 @@ +if (self.name !== "tsWorker") { + throw Error(`Invalid worker name: ${self.name}, expected tsWorker`); +} + +onmessage = function (e) { + postMessage(e.data); + close(); +}; diff --git a/cli/tests/testdata/workers/throwing_worker.js b/cli/tests/testdata/workers/throwing_worker.js new file mode 100644 index 000000000..56ee4ff88 --- /dev/null +++ b/cli/tests/testdata/workers/throwing_worker.js @@ -0,0 +1,2 @@ +// This worker just throws error when it's being executed +throw Error("Thrown error"); diff --git a/cli/tests/testdata/workers/worker_crypto.js b/cli/tests/testdata/workers/worker_crypto.js new file mode 100644 index 000000000..4398ad068 --- /dev/null +++ b/cli/tests/testdata/workers/worker_crypto.js @@ -0,0 +1,5 @@ +self.crypto.getRandomValues(new Uint8Array(16)); + +onmessage = function () { + postMessage(!!self.crypto); +}; diff --git a/cli/tests/testdata/workers/worker_error.ts b/cli/tests/testdata/workers/worker_error.ts new file mode 100644 index 000000000..696680de8 --- /dev/null +++ b/cli/tests/testdata/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/testdata/workers/worker_error.ts.out b/cli/tests/testdata/workers/worker_error.ts.out new file mode 100644 index 000000000..4a8e92f00 --- /dev/null +++ b/cli/tests/testdata/workers/worker_error.ts.out @@ -0,0 +1,5 @@ +[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. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/worker_event_handlers.js b/cli/tests/testdata/workers/worker_event_handlers.js new file mode 100644 index 000000000..c8976f79e --- /dev/null +++ b/cli/tests/testdata/workers/worker_event_handlers.js @@ -0,0 +1,23 @@ +self.onmessage = (evt) => { + console.log("Target from self.onmessage:", String(evt.target)); +}; + +self.addEventListener("message", (evt) => { + console.log("Target from message event listener:", String(evt.target)); + + // Throw an error here so the global's error event will fire. + throw new Error("Some error message"); +}); + +self.onerror = (...args) => { + console.log("Arguments from self.onerror:", args); + return true; +}; + +self.addEventListener("error", (evt) => { + // Returning true from self.onerror means that subsequent event listeners + // should see the event as canceled. + console.log("Is event canceled?:", evt.defaultPrevented); + + self.close(); +}); diff --git a/cli/tests/testdata/workers/worker_globals.ts b/cli/tests/testdata/workers/worker_globals.ts new file mode 100644 index 000000000..90e369e41 --- /dev/null +++ b/cli/tests/testdata/workers/worker_globals.ts @@ -0,0 +1,13 @@ +onmessage = function () { + postMessage( + [ + self instanceof DedicatedWorkerGlobalScope, + self instanceof WorkerGlobalScope, + self instanceof EventTarget, + // TODO(nayeemrmn): Add `WorkerNavigator` to deno_lint globals. + // deno-lint-ignore no-undef + navigator instanceof WorkerNavigator, + ].join(", "), + ); + close(); +}; diff --git a/cli/tests/testdata/workers/worker_large_message.js b/cli/tests/testdata/workers/worker_large_message.js new file mode 100644 index 000000000..a1ddae4f9 --- /dev/null +++ b/cli/tests/testdata/workers/worker_large_message.js @@ -0,0 +1,14 @@ +// Copyright 2020 the Deno authors. All rights reserved. MIT license. + +const dataSmall = ""; +const dataLarge = "x".repeat(10 * 1024); + +onmessage = function (_e) { + for (let i = 0; i <= 10; i++) { + if (i % 2 == 0) { + postMessage(dataLarge); + } else { + postMessage(dataSmall); + } + } +}; diff --git a/cli/tests/testdata/workers/worker_location.ts b/cli/tests/testdata/workers/worker_location.ts new file mode 100644 index 000000000..c3c1bb26f --- /dev/null +++ b/cli/tests/testdata/workers/worker_location.ts @@ -0,0 +1,6 @@ +onmessage = function () { + postMessage( + `${location.href}, ${location instanceof WorkerLocation}`, + ); + close(); +}; diff --git a/cli/tests/testdata/workers/worker_nested_error.ts b/cli/tests/testdata/workers/worker_nested_error.ts new file mode 100644 index 000000000..aba2011be --- /dev/null +++ b/cli/tests/testdata/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/testdata/workers/worker_nested_error.ts.out b/cli/tests/testdata/workers/worker_nested_error.ts.out new file mode 100644 index 000000000..4a8e92f00 --- /dev/null +++ b/cli/tests/testdata/workers/worker_nested_error.ts.out @@ -0,0 +1,5 @@ +[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. + at Worker.#pollControl ([WILDCARD]) diff --git a/cli/tests/testdata/workers/worker_structured_cloning.ts b/cli/tests/testdata/workers/worker_structured_cloning.ts new file mode 100644 index 000000000..eb1719a9a --- /dev/null +++ b/cli/tests/testdata/workers/worker_structured_cloning.ts @@ -0,0 +1,15 @@ +// More info on structured cloning can be found here: +// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm + +self.onmessage = () => { + const arr = ["a", true, 432]; + const set = new Set([1, 3, 5, 7, 9]); + const selfReference = { + a: arr, + b: arr, + c: set, + }; + // deno-lint-ignore no-explicit-any + (selfReference as any).self = selfReference; + self.postMessage(selfReference); +}; diff --git a/cli/tests/testdata/workers/worker_types.ts b/cli/tests/testdata/workers/worker_types.ts new file mode 100644 index 000000000..b67a3b782 --- /dev/null +++ b/cli/tests/testdata/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/testdata/workers/worker_unstable.ts b/cli/tests/testdata/workers/worker_unstable.ts new file mode 100644 index 000000000..a5b5f7ba2 --- /dev/null +++ b/cli/tests/testdata/workers/worker_unstable.ts @@ -0,0 +1,5 @@ +console.log(Deno.permissions.query); +console.log(Deno.emit); +self.onmessage = () => { + self.close(); +}; diff --git a/cli/tests/testdata/workers/worker_with_top_level_await.ts b/cli/tests/testdata/workers/worker_with_top_level_await.ts new file mode 100644 index 000000000..1d20bb736 --- /dev/null +++ b/cli/tests/testdata/workers/worker_with_top_level_await.ts @@ -0,0 +1,15 @@ +function delay(ms: number) { + return new Promise<void>((resolve) => { + setTimeout(() => { + resolve(); + }, ms); + }); +} + +onmessage = (_e: MessageEvent) => { + postMessage("triggered worker handler"); + close(); +}; +postMessage("ready"); +await delay(1000); +postMessage("never"); |
