diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/asyncshutdown/tests/xpcshell/head.js | 184 |
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); +} |