aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-07-28 16:44:28 -0700
committerGravatar GitHub <noreply@github.com> 2023-07-28 16:44:28 -0700
commitd614fdfaac13346d71ecf24712abaefe8224687d (patch)
tree2fce0943c663db984755df123d96d788984cad22 /test
parent0a4e476a7c08005e242ed48f3f27895e55deacc9 (diff)
downloadbun-d614fdfaac13346d71ecf24712abaefe8224687d.tar.gz
bun-d614fdfaac13346d71ecf24712abaefe8224687d.tar.zst
bun-d614fdfaac13346d71ecf24712abaefe8224687d.zip
`MessageChannel` and `MessagePort` (#3860)
* copy and format * copy * copy * cleanup * some tests * spellcheck * add types * don't lock getting contextId * array buffer test
Diffstat (limited to 'test')
-rw-r--r--test/js/web/workers/create-port-worker.js6
-rw-r--r--test/js/web/workers/message-channel.test.ts254
-rw-r--r--test/js/web/workers/receive-port-worker.js9
-rw-r--r--test/js/web/workers/structured-clone.test.ts2
4 files changed, 270 insertions, 1 deletions
diff --git a/test/js/web/workers/create-port-worker.js b/test/js/web/workers/create-port-worker.js
new file mode 100644
index 000000000..6e8709de0
--- /dev/null
+++ b/test/js/web/workers/create-port-worker.js
@@ -0,0 +1,6 @@
+var channel = new MessageChannel();
+channel.port1.onmessage = e => {
+ channel.port1.postMessage("done!");
+};
+
+postMessage(channel.port2, { transfer: [channel.port2] });
diff --git a/test/js/web/workers/message-channel.test.ts b/test/js/web/workers/message-channel.test.ts
new file mode 100644
index 000000000..7d3e3cc25
--- /dev/null
+++ b/test/js/web/workers/message-channel.test.ts
@@ -0,0 +1,254 @@
+test("simple usage", done => {
+ var channel = new MessageChannel();
+ var port1 = channel.port1;
+ var port2 = channel.port2;
+
+ port2.onmessage = function (e) {
+ expect(e.data).toEqual("hello");
+ done();
+ };
+
+ port1.postMessage("hello");
+});
+
+test("transfer message port", done => {
+ var channel = new MessageChannel();
+ var anotherChannel = new MessageChannel();
+ var port1 = channel.port1;
+ var port2 = channel.port2;
+
+ port2.onmessage = function (e) {
+ expect(e.data).toEqual("hello");
+ expect(e.ports).toHaveLength(1);
+ expect(e.ports[0]).toBeInstanceOf(MessagePort);
+ done();
+ };
+
+ port1.postMessage("hello", [anotherChannel.port2]);
+});
+
+test("tranfer array buffer", done => {
+ var channel = new MessageChannel();
+ var port1 = channel.port1;
+ var port2 = channel.port2;
+
+ port2.onmessage = function (e) {
+ expect(e.data).toBeInstanceOf(ArrayBuffer);
+ expect(e.data.byteLength).toEqual(8);
+ done();
+ };
+
+ const buffer = new ArrayBuffer(8);
+
+ port1.postMessage(buffer, [buffer]);
+});
+
+test("non-transferable", () => {
+ var channel = new MessageChannel();
+ channel.port2.onmessage = function (e) {
+ // not reached
+ expect(1).toBe(2);
+ };
+ expect(() => {
+ channel.port1.postMessage("hello", [channel.port1]);
+ }).toThrow();
+ expect(() => {
+ channel.port1.postMessage("hello", [channel.port2]);
+ }).toThrow();
+});
+
+test("transfer message ports and post messages", done => {
+ var c1 = new MessageChannel();
+ var c2 = new MessageChannel();
+
+ c1.port1.onmessage = e => {
+ var port = e.ports[0];
+ expect(port).toBeInstanceOf(MessagePort);
+ expect(e.data).toEqual("hello from channel 1 port 2");
+ port.onmessage = e => {
+ expect(e.data).toEqual("hello from channel 1 port 2");
+ done();
+ };
+ port.postMessage("hello from channel 1 port 1", [c1.port1]);
+ };
+
+ c1.port2.onmessage = e => {
+ var port = e.ports[0];
+ expect(port).toBeInstanceOf(MessagePort);
+ expect(e.data).toEqual("hello from channel 2 port 1");
+ port.postMessage("hello from channel 1 port 2");
+ };
+
+ c2.port1.onmessage = e => {
+ var port = e.ports[0];
+ expect(port).toBeInstanceOf(MessagePort);
+ expect(e.data).toEqual("hello from channel 1 port 1");
+ port.postMessage("hello from channel 2 port 1", [c2.port1]);
+ };
+
+ c2.port2.onmessage = e => {
+ // should not be reached. onmessage defined in c1.port1 should be called instead
+ expect(1).toBe(2);
+ };
+
+ c1.port2.postMessage("hello from channel 1 port 2", [c2.port2]);
+});
+
+test("message channel created on main thread", done => {
+ var worker = new Worker(new URL("receive-port-worker.js", import.meta.url).href);
+ worker.onerror = e => {
+ expect(1).toBe(2);
+ done();
+ };
+ var channel = new MessageChannel();
+ channel.port1.onmessage = e => {
+ if (e.data === "done!") return done();
+ expect(e.data).toEqual("received port!");
+ channel.port1.postMessage("more message!");
+ };
+ worker.postMessage(channel.port2, { transfer: [channel.port2] });
+});
+
+test("message channel created on other thread", done => {
+ var worker = new Worker(new URL("create-port-worker.js", import.meta.url).href);
+ worker.onerror = e => {
+ expect(1).toBe(2);
+ done();
+ };
+ worker.onmessage = e => {
+ expect(e.data).toBeInstanceOf(MessagePort);
+ var port = e.data;
+ port.onmessage = e => {
+ expect(e.data).toEqual("done!");
+ done();
+ };
+ port.postMessage("hello from main thread");
+ };
+});
+
+test("many message channels", done => {
+ var channel = new MessageChannel();
+ var channel2 = new MessageChannel();
+ var channel3 = new MessageChannel();
+ var channel4 = new MessageChannel();
+
+ channel.port1.postMessage("noport");
+ channel.port1.postMessage("zero ports", []);
+ channel.port1.postMessage("two ports", [channel2.port1, channel2.port2]);
+
+ // Now test various failure cases
+ expect(() => {
+ channel.port1.postMessage("same port", [channel.port1]);
+ }).toThrow();
+ expect(() => {
+ channel.port1.postMessage("entangled port", [channel.port2]);
+ }).toThrow();
+ expect(() => {
+ channel.port1.postMessage("null port", [channel3.port1, null, channel3.port2]);
+ }).toThrow();
+ expect(() => {
+ channel.port1.postMessage("notAPort", [channel3.port1, {}, channel3.port2]);
+ }).toThrow();
+ expect(() => {
+ channel.port1.postMessage("duplicate port", [channel3.port1, channel3.port1]);
+ }).toThrow();
+
+ // Should be OK to send channel3.port1 (should not have been disentangled by the previous failed calls).
+ expect(() => {
+ channel.port1.postMessage("entangled ports", [channel3.port1, channel3.port2]);
+ }).not.toThrow();
+
+ expect(() => {
+ channel.port1.postMessage("notAnArray", "foo");
+ }).toThrow();
+ expect(() => {
+ channel.port1.postMessage("notASequence", [{ length: 3 }]);
+ }).toThrow();
+
+ // Should not crash (we should figure out that the array contains undefined
+ // entries).
+ var largePortArray = [];
+ largePortArray[1234567890] = channel4.port1;
+ expect(() => {
+ channel.port1.postMessage("largeSequence", largePortArray);
+ }).toThrow();
+
+ channel.port1.postMessage("done");
+
+ function testTransfers(done) {
+ var channel0 = new MessageChannel();
+
+ var c1 = new MessageChannel();
+ channel0.port1.postMessage({ id: "send-port", port: c1.port1 }, [c1.port1]);
+ var c2 = new MessageChannel();
+ channel0.port1.postMessage({ id: "send-port-twice", port0: c2.port1, port1: c2.port1 }, [c2.port1]);
+ var c3 = new MessageChannel();
+ channel0.port1.postMessage({ id: "send-two-ports", port0: c3.port1, port1: c3.port2 }, [c3.port1, c3.port2]);
+ var c4 = new MessageChannel();
+
+ // Sending host objects should throw
+ expect(() => {
+ channel0.port1.postMessage({ id: "host-object", hostObject: c3, port: c4.port1 }, [c4.port1]);
+ }).toThrow();
+
+ // Sending Function object should throw
+ expect(() => {
+ var f1 = function () {};
+ channel0.port1.postMessage({ id: "function-object", function: f1, port: c4.port1 }, [c4.port1]);
+ }).toThrow();
+
+ // Sending Error object should not throw
+ // expect(() => {
+ // var err = new Error();
+ // channel0.port1.postMessage({ id: "error-object", error: err, port: c4.port1 }, [c4.port1]);
+ // }).not.toThrow();
+
+ c4.port1.postMessage("Should succeed");
+ channel0.port1.postMessage({ id: "done" });
+
+ channel0.port2.onmessage = function (event) {
+ if (event.data.id == "send-port") {
+ expect(event.ports.length).toBeGreaterThan(0);
+ expect(event.ports[0]).toBe(event.data.port);
+ } else if (event.data.id == "error-object") {
+ expect(event.data.error).toBeInstanceOf(Error);
+ } else if (event.data.id == "send-port-twice") {
+ expect(event.ports).toBeDefined();
+ expect(event.ports.length).toBe(1);
+ expect(event.ports[0]).toBe(event.data.port0);
+ expect(event.ports[0]).toBe(event.data.port1);
+ } else if (event.data.id == "send-two-ports") {
+ expect(event.ports).toBeDefined();
+ expect(event.ports.length).toBe(2);
+ expect(event.ports[0]).toBe(event.data.port0);
+ expect(event.ports[1]).toBe(event.data.port1);
+ } else if (event.data.id == "done") {
+ done();
+ } else {
+ // should not be reached
+ expect(1).toBe(2);
+ }
+ };
+ }
+
+ channel.port2.onmessage = function (event) {
+ if (event.data == "noport") {
+ expect(event.ports).toBeDefined();
+ expect(event.ports.length).toBe(0);
+ } else if (event.data == "zero ports") {
+ expect(event.ports).toBeDefined();
+ expect(event.ports.length).toBe(0);
+ } else if (event.data == "two ports") {
+ expect(event.ports).toBeDefined();
+ expect(event.ports.length).toBe(2);
+ } else if (event.data == "entangled ports") {
+ expect(event.ports).toBeDefined();
+ expect(event.ports.length).toBe(2);
+ } else if (event.data == "done") {
+ testTransfers(done);
+ } else {
+ // should not be reached
+ expect(1).toBe(2);
+ }
+ };
+});
diff --git a/test/js/web/workers/receive-port-worker.js b/test/js/web/workers/receive-port-worker.js
new file mode 100644
index 000000000..871fc4405
--- /dev/null
+++ b/test/js/web/workers/receive-port-worker.js
@@ -0,0 +1,9 @@
+onmessage = e => {
+ if (e.data instanceof MessagePort) {
+ var port = e.data;
+ port.onmessage = e => {
+ port.postMessage("done!");
+ };
+ port.postMessage("received port!");
+ }
+};
diff --git a/test/js/web/workers/structured-clone.test.ts b/test/js/web/workers/structured-clone.test.ts
index 61a0c1320..bdc7fcfea 100644
--- a/test/js/web/workers/structured-clone.test.ts
+++ b/test/js/web/workers/structured-clone.test.ts
@@ -183,7 +183,7 @@ describe("structured clone", () => {
});
});
- describe("transferrables", () => {
+ describe("transferables", () => {
test("ArrayBuffer", () => {
const buffer = Uint8Array.from([1]).buffer;
const cloned = structuredClone(buffer, { transfer: [buffer] });