summaryrefslogtreecommitdiff
path: root/cli/tests/testdata/workers
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tests/testdata/workers')
-rw-r--r--cli/tests/testdata/workers/bench_large_message.ts31
-rw-r--r--cli/tests/testdata/workers/bench_round_robin.ts68
-rw-r--r--cli/tests/testdata/workers/bench_startup.ts33
-rw-r--r--cli/tests/testdata/workers/bench_worker.ts21
-rw-r--r--cli/tests/testdata/workers/broadcast_channel.ts5
-rw-r--r--cli/tests/testdata/workers/busy_worker.js8
-rw-r--r--cli/tests/testdata/workers/close_race_worker.js6
-rw-r--r--cli/tests/testdata/workers/deno_worker.ts7
-rw-r--r--cli/tests/testdata/workers/dynamic_remote.ts2
-rw-r--r--cli/tests/testdata/workers/error.ts5
-rw-r--r--cli/tests/testdata/workers/event_worker.js7
-rw-r--r--cli/tests/testdata/workers/event_worker_scope.js43
-rw-r--r--cli/tests/testdata/workers/fetching_worker.js6
-rw-r--r--cli/tests/testdata/workers/http_worker.js11
-rw-r--r--cli/tests/testdata/workers/immediately_close_worker.js1
-rw-r--r--cli/tests/testdata/workers/message_port.ts14
-rw-r--r--cli/tests/testdata/workers/nested_worker.js18
-rw-r--r--cli/tests/testdata/workers/no_permissions_worker.js17
-rw-r--r--cli/tests/testdata/workers/non_deno_worker.js7
-rw-r--r--cli/tests/testdata/workers/nonexistent_worker.out3
-rw-r--r--cli/tests/testdata/workers/nonexistent_worker.ts5
-rw-r--r--cli/tests/testdata/workers/parent_read_check_granular_worker.js41
-rw-r--r--cli/tests/testdata/workers/parent_read_check_worker.js27
-rw-r--r--cli/tests/testdata/workers/permissions_blob_local.ts6
-rw-r--r--cli/tests/testdata/workers/permissions_blob_local.ts.out4
-rw-r--r--cli/tests/testdata/workers/permissions_blob_remote.ts4
-rw-r--r--cli/tests/testdata/workers/permissions_blob_remote.ts.out4
-rw-r--r--cli/tests/testdata/workers/permissions_data_local.ts7
-rw-r--r--cli/tests/testdata/workers/permissions_data_local.ts.out4
-rw-r--r--cli/tests/testdata/workers/permissions_data_remote.ts5
-rw-r--r--cli/tests/testdata/workers/permissions_data_remote.ts.out4
-rw-r--r--cli/tests/testdata/workers/permissions_dynamic_remote.ts11
-rw-r--r--cli/tests/testdata/workers/permissions_dynamic_remote.ts.out6
-rw-r--r--cli/tests/testdata/workers/permissions_remote_remote.ts3
-rw-r--r--cli/tests/testdata/workers/permissions_remote_remote.ts.out4
-rw-r--r--cli/tests/testdata/workers/post_undefined.ts5
-rw-r--r--cli/tests/testdata/workers/racy_worker.js25
-rw-r--r--cli/tests/testdata/workers/read_check_granular_worker.js11
-rw-r--r--cli/tests/testdata/workers/read_check_worker.js7
-rw-r--r--cli/tests/testdata/workers/shared_array_buffer.ts9
-rw-r--r--cli/tests/testdata/workers/sibling_worker.js4
-rw-r--r--cli/tests/testdata/workers/static_remote.ts2
-rw-r--r--cli/tests/testdata/workers/test.ts852
-rw-r--r--cli/tests/testdata/workers/test.ts.out3
-rw-r--r--cli/tests/testdata/workers/test_worker.js19
-rw-r--r--cli/tests/testdata/workers/test_worker.ts8
-rw-r--r--cli/tests/testdata/workers/throwing_worker.js2
-rw-r--r--cli/tests/testdata/workers/worker_crypto.js5
-rw-r--r--cli/tests/testdata/workers/worker_error.ts5
-rw-r--r--cli/tests/testdata/workers/worker_error.ts.out5
-rw-r--r--cli/tests/testdata/workers/worker_event_handlers.js23
-rw-r--r--cli/tests/testdata/workers/worker_globals.ts13
-rw-r--r--cli/tests/testdata/workers/worker_large_message.js14
-rw-r--r--cli/tests/testdata/workers/worker_location.ts6
-rw-r--r--cli/tests/testdata/workers/worker_nested_error.ts5
-rw-r--r--cli/tests/testdata/workers/worker_nested_error.ts.out5
-rw-r--r--cli/tests/testdata/workers/worker_structured_cloning.ts15
-rw-r--r--cli/tests/testdata/workers/worker_types.ts4
-rw-r--r--cli/tests/testdata/workers/worker_unstable.ts5
-rw-r--r--cli/tests/testdata/workers/worker_with_top_level_await.ts15
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");