diff options
Diffstat (limited to 'toolkit/components/promiseworker/tests')
4 files changed, 217 insertions, 0 deletions
diff --git a/toolkit/components/promiseworker/tests/xpcshell/data/chrome.manifest b/toolkit/components/promiseworker/tests/xpcshell/data/chrome.manifest new file mode 100644 index 0000000000..9e5dd29b22 --- /dev/null +++ b/toolkit/components/promiseworker/tests/xpcshell/data/chrome.manifest @@ -0,0 +1 @@ +content promiseworker ./ diff --git a/toolkit/components/promiseworker/tests/xpcshell/data/worker.js b/toolkit/components/promiseworker/tests/xpcshell/data/worker.js new file mode 100644 index 0000000000..ffb3473713 --- /dev/null +++ b/toolkit/components/promiseworker/tests/xpcshell/data/worker.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/chrome-worker */ + +"use strict"; + +// Trivial worker definition + +/* import-globals-from /toolkit/components/workerloader/require.js */ +importScripts("resource://gre/modules/workers/require.js"); +var PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js"); + +var worker = new PromiseWorker.AbstractWorker(); +worker.dispatch = function (method, args = []) { + return Agent[method](...args); +}; +worker.postMessage = function (...args) { + self.postMessage(...args); +}; +worker.close = function () { + self.close(); +}; +worker.log = function (...args) { + dump("Worker: " + args.join(" ") + "\n"); +}; +self.addEventListener("message", msg => worker.handleMessage(msg)); +self.addEventListener("unhandledrejection", function (error) { + throw error.reason; +}); + +var Agent = { + bounce(...args) { + return args; + }, + + throwError(msg, ...args) { + throw new Error(msg); + }, +}; diff --git a/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js b/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js new file mode 100644 index 0000000000..f7581b664f --- /dev/null +++ b/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js @@ -0,0 +1,168 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +"use strict"; + +const { BasePromiseWorker } = ChromeUtils.importESModule( + "resource://gre/modules/PromiseWorker.sys.mjs" +); +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +// Worker must be loaded from a chrome:// uri, not a file:// +// uri, so we first need to load it. + +var WORKER_SOURCE_URI = "chrome://promiseworker/content/worker.js"; +do_load_manifest("data/chrome.manifest"); +var worker = new BasePromiseWorker(WORKER_SOURCE_URI); +worker.log = function (...args) { + info("Controller: " + args.join(" ")); +}; + +// Test that simple messages work +add_task(async function test_simple_args() { + let message = ["test_simple_args", Math.random()]; + let result = await worker.post("bounce", message); + Assert.equal(JSON.stringify(result), JSON.stringify(message)); +}); + +// Test that it works when we don't provide a message +add_task(async function test_no_args() { + let result = await worker.post("bounce"); + Assert.equal(JSON.stringify(result), JSON.stringify([])); +}); + +// Test that messages with promise work +add_task(async function test_promise_args() { + let message = ["test_promise_args", Promise.resolve(Math.random())]; + let stringified = JSON.stringify(await Promise.resolve(Promise.all(message))); + let result = await worker.post("bounce", message); + Assert.equal(JSON.stringify(result), stringified); +}); + +// Test that messages with delayed promise work +add_task(async function test_delayed_promise_args() { + let promise = new Promise(resolve => + setTimeout(() => resolve(Math.random()), 10) + ); + let message = ["test_delayed_promise_args", promise]; + let stringified = JSON.stringify(await Promise.resolve(Promise.all(message))); + let result = await worker.post("bounce", message); + Assert.equal(JSON.stringify(result), stringified); +}); + +// Test that messages with rejected promise cause appropriate errors +add_task(async function test_rejected_promise_args() { + let error = new Error(); + let message = ["test_promise_args", Promise.reject(error)]; + try { + await worker.post("bounce", message); + do_throw("I shound have thrown an error by now"); + } catch (ex) { + if (ex != error) { + throw ex; + } + info("I threw the right error"); + } +}); + +// Test that we can transfer to the worker using argument `transfer` +add_task(async function test_transfer_args() { + let array = new Uint8Array(4); + for (let i = 0; i < 4; ++i) { + array[i] = i; + } + Assert.equal(array.buffer.byteLength, 4, "The buffer is not detached yet"); + + let result = ( + await worker.post("bounce", [array.buffer], [], [array.buffer]) + )[0]; + + // Check that the buffer has been sent + Assert.equal(array.buffer.byteLength, 0, "The buffer has been detached"); + + // Check that the result is correct + Assert.equal(result.byteLength, 4, "The result has the right size"); + let array2 = new Uint8Array(result); + for (let i = 0; i < 4; ++i) { + Assert.equal(array2[i], i); + } +}); + +// Test that we can transfer to the worker using an instance of `Meta` +add_task(async function test_transfer_with_meta() { + let array = new Uint8Array(4); + for (let i = 0; i < 4; ++i) { + array[i] = i; + } + Assert.equal(array.buffer.byteLength, 4, "The buffer is not detached yet"); + + let message = new BasePromiseWorker.Meta(array, { + transfers: [array.buffer], + }); + let result = (await worker.post("bounce", [message]))[0]; + + // Check that the buffer has been sent + Assert.equal(array.buffer.byteLength, 0, "The buffer has been detached"); + + // Check that the result is correct + Assert.equal( + Object.prototype.toString.call(result), + "[object Uint8Array]", + "The result appears to be a Typed Array" + ); + Assert.equal(result.byteLength, 4, "The result has the right size"); + + for (let i = 0; i < 4; ++i) { + Assert.equal(result[i], i); + } +}); + +add_task(async function test_throw_error() { + try { + await worker.post("throwError", ["error message"]); + Assert.ok(false, "should have thrown"); + } catch (ex) { + Assert.equal(ex.message, "Error: error message"); + } +}); + +add_task(async function test_terminate() { + let previousWorker = worker._worker; + + // Send two messages that we'll expect to be rejected. + let message = ["test_simple_args", Math.random()]; + let promise1 = worker.post("bounce", message); + let promise2 = worker.post("throwError", ["error message"]); + // Skip a few beats so we can be sure that the two messages are in the queue. + await Promise.resolve(); + await Promise.resolve(); + + worker.terminate(); + + await Assert.rejects( + promise1, + /worker terminated/, + "Pending promise should be rejected" + ); + await Assert.rejects( + promise2, + /worker terminated/, + "Pending promise should be rejected" + ); + + // Unfortunately, there's no real way to check whether a terminate worked from + // the JS API. We'll just have to assume it worked. + + // Post and test a simple message to ensure that the worker has been re-instantiated. + message = ["test_simple_args", Math.random()]; + let result = await worker.post("bounce", message); + Assert.equal(JSON.stringify(result), JSON.stringify(message)); + Assert.notEqual( + worker._worker, + previousWorker, + "ChromeWorker instances should differ" + ); +}); diff --git a/toolkit/components/promiseworker/tests/xpcshell/xpcshell.ini b/toolkit/components/promiseworker/tests/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..4922f3f091 --- /dev/null +++ b/toolkit/components/promiseworker/tests/xpcshell/xpcshell.ini @@ -0,0 +1,8 @@ +[DEFAULT] +head= +skip-if = toolkit == 'android' +support-files= + data/worker.js + data/chrome.manifest + +[test_Promise.js] |