Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
/* 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");
const UUID = crypto.randomUUID();
const SOME_ARRAY = new Uint8Array(4);
for (let i = 0; i < 4; ++i) {
SOME_ARRAY[i] = i;
}
async function echo(message) {
return new BasePromiseWorker.Meta([message, UUID, SOME_ARRAY.buffer], {
transfers: [SOME_ARRAY.buffer],
});
}
var worker = new BasePromiseWorker(WORKER_SOURCE_URI, {}, { echo });
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"
);
});
function cloneArrayBuffer(original) {
const clone = new ArrayBuffer(original.byteLength);
const originalView = new Uint8Array(original);
const cloneView = new Uint8Array(clone);
cloneView.set(originalView);
return clone;
}
add_task(async function test_bidirectional() {
// Before we transfer the array, we clone it
const arrayCopy = cloneArrayBuffer(SOME_ARRAY.buffer);
let message = ["test_simple_args", Math.random()];
// Checking the array buffer size
Assert.equal(
SOME_ARRAY.buffer.byteLength,
4,
"The buffer is not detached yet"
);
let result = await worker.post("bounceWithExtraCalls", message);
// After the post call, the array was transferred and SOME_ARRAY should be empty
Assert.equal(SOME_ARRAY.buffer.byteLength, 0, "The buffer has been detached");
// The echo() function in the worker adds to the message a string, an uuid and has the transferred array
message.push(["Posting something unrelated", UUID, arrayCopy]);
Assert.deepEqual(result, message);
});