summaryrefslogtreecommitdiffstats
path: root/toolkit/components/asyncshutdown/tests/xpcshell/head.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/components/asyncshutdown/tests/xpcshell/head.js184
1 files changed, 184 insertions, 0 deletions
diff --git a/toolkit/components/asyncshutdown/tests/xpcshell/head.js b/toolkit/components/asyncshutdown/tests/xpcshell/head.js
new file mode 100644
index 0000000000..5cbf2359a2
--- /dev/null
+++ b/toolkit/components/asyncshutdown/tests/xpcshell/head.js
@@ -0,0 +1,184 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { PromiseUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/PromiseUtils.sys.mjs"
+);
+
+var { AsyncShutdown } = ChromeUtils.importESModule(
+ "resource://gre/modules/AsyncShutdown.sys.mjs"
+);
+
+var asyncShutdownService = Cc[
+ "@mozilla.org/async-shutdown-service;1"
+].getService(Ci.nsIAsyncShutdownService);
+
+Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true);
+
+/**
+ * Utility function used to provide the same API for various sources
+ * of async shutdown barriers.
+ *
+ * @param {string} kind One of
+ * - "phase" to test an AsyncShutdown phase;
+ * - "barrier" to test an instance of AsyncShutdown.Barrier;
+ * - "xpcom-barrier" to test an instance of nsIAsyncShutdownBarrier;
+ * - "xpcom-barrier-unwrapped" to test the field `jsclient` of a nsIAsyncShutdownClient.
+ *
+ * @return An object with the following methods:
+ * - addBlocker() - the same method as AsyncShutdown phases and barrier clients
+ * - wait() - trigger the resolution of the lock
+ */
+function makeLock(kind) {
+ if (kind == "phase") {
+ let topic = "test-Phase-" + ++makeLock.counter;
+ let phase = AsyncShutdown._getPhase(topic);
+ return {
+ addBlocker(...args) {
+ return phase.addBlocker(...args);
+ },
+ removeBlocker(blocker) {
+ return phase.removeBlocker(blocker);
+ },
+ wait() {
+ Services.obs.notifyObservers(null, topic);
+ return Promise.resolve();
+ },
+ get isClosed() {
+ return phase.isClosed;
+ },
+ };
+ } else if (kind == "barrier") {
+ let name = "test-Barrier-" + ++makeLock.counter;
+ let barrier = new AsyncShutdown.Barrier(name);
+ return {
+ addBlocker: barrier.client.addBlocker,
+ removeBlocker: barrier.client.removeBlocker,
+ wait() {
+ return barrier.wait();
+ },
+ get isClosed() {
+ return barrier.client.isClosed;
+ },
+ };
+ } else if (kind == "xpcom-barrier") {
+ let name = "test-xpcom-Barrier-" + ++makeLock.counter;
+ let barrier = asyncShutdownService.makeBarrier(name);
+ return {
+ addBlocker(blockerName, condition, state) {
+ if (condition == null) {
+ // Slight trick as `null` or `undefined` cannot be used as keys
+ // for `xpcomMap`. Note that this has no incidence on the result
+ // of the test as the XPCOM interface imposes that the condition
+ // is a method, so it cannot be `null`/`undefined`.
+ condition = "<this case can't happen with the xpcom interface>";
+ }
+ let blocker = makeLock.xpcomMap.get(condition);
+ if (!blocker) {
+ blocker = {
+ name: blockerName,
+ state,
+ blockShutdown(aBarrierClient) {
+ return (async function () {
+ try {
+ if (typeof condition == "function") {
+ await Promise.resolve(condition());
+ } else {
+ await Promise.resolve(condition);
+ }
+ } finally {
+ aBarrierClient.removeBlocker(blocker);
+ }
+ })();
+ },
+ };
+ makeLock.xpcomMap.set(condition, blocker);
+ }
+ let { fileName, lineNumber, stack } = new Error();
+ return barrier.client.addBlocker(blocker, fileName, lineNumber, stack);
+ },
+ removeBlocker(condition) {
+ let blocker = makeLock.xpcomMap.get(condition);
+ if (!blocker) {
+ return;
+ }
+ barrier.client.removeBlocker(blocker);
+ },
+ wait() {
+ return new Promise(resolve => {
+ barrier.wait(resolve);
+ });
+ },
+ get isClosed() {
+ return barrier.client.isClosed;
+ },
+ };
+ } else if ("unwrapped-xpcom-barrier") {
+ let name = "unwrapped-xpcom-barrier-" + ++makeLock.counter;
+ let barrier = asyncShutdownService.makeBarrier(name);
+ let client = barrier.client.jsclient;
+ return {
+ addBlocker: client.addBlocker,
+ removeBlocker: client.removeBlocker,
+ wait() {
+ return new Promise(resolve => {
+ barrier.wait(resolve);
+ });
+ },
+ get isClosed() {
+ return client.isClosed;
+ },
+ };
+ }
+ throw new TypeError("Unknown kind " + kind);
+}
+makeLock.counter = 0;
+makeLock.xpcomMap = new Map(); // Note: Not a WeakMap as we wish to handle non-gc-able keys (e.g. strings)
+
+/**
+ * An asynchronous task that takes several ticks to complete.
+ *
+ * @param {*=} resolution The value with which the resulting promise will be
+ * resolved once the task is complete. This may be a rejected promise,
+ * in which case the resulting promise will itself be rejected.
+ * @param {object=} outResult An object modified by side-effect during the task.
+ * Initially, its field |isFinished| is set to |false|. Once the task is
+ * complete, its field |isFinished| is set to |true|.
+ *
+ * @return {promise} A promise fulfilled once the task is complete
+ */
+function longRunningAsyncTask(resolution = undefined, outResult = {}) {
+ outResult.isFinished = false;
+ if (!("countFinished" in outResult)) {
+ outResult.countFinished = 0;
+ }
+ return new Promise(resolve => {
+ do_timeout(100, function () {
+ ++outResult.countFinished;
+ outResult.isFinished = true;
+ resolve(resolution);
+ });
+ });
+}
+
+function get_exn(f) {
+ try {
+ f();
+ return null;
+ } catch (ex) {
+ return ex;
+ }
+}
+
+function do_check_exn(exn, constructor) {
+ Assert.notEqual(exn, null);
+ if (exn.name == constructor) {
+ Assert.equal(exn.constructor.name, constructor);
+ return;
+ }
+ info("Wrong error constructor");
+ info(exn.constructor.name);
+ info(exn.stack);
+ Assert.ok(false);
+}