diff options
Diffstat (limited to 'toolkit/components/asyncshutdown/tests/xpcshell/head.js')
-rw-r--r-- | toolkit/components/asyncshutdown/tests/xpcshell/head.js | 174 |
1 files changed, 174 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 000000000..9de489808 --- /dev/null +++ b/toolkit/components/asyncshutdown/tests/xpcshell/head.js @@ -0,0 +1,174 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +var Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cr = Components.results; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/AsyncShutdown.jsm"); + +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: function(...args) { + return phase.addBlocker(...args); + }, + removeBlocker: function(blocker) { + return phase.removeBlocker(blocker); + }, + wait: function() { + Services.obs.notifyObservers(null, topic, null); + return Promise.resolve(); + } + }; + } 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: function() { + return barrier.wait(); + } + }; + } else if (kind == "xpcom-barrier") { + let name = "test-xpcom-Barrier-" + ++makeLock.counter; + let barrier = asyncShutdownService.makeBarrier(name); + return { + addBlocker: function(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: state, + blockShutdown: function(aBarrierClient) { + return Task.spawn(function*() { + try { + if (typeof condition == "function") { + yield Promise.resolve(condition()); + } else { + yield 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: function(condition) { + let blocker = makeLock.xpcomMap.get(condition); + if (!blocker) { + return; + } + barrier.client.removeBlocker(blocker); + }, + wait: function() { + return new Promise(resolve => { + barrier.wait(resolve); + }); + } + }; + } 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: function() { + return new Promise(resolve => { + barrier.wait(resolve); + }); + } + }; + } + 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; + } + let deferred = Promise.defer(); + do_timeout(100, function() { + ++outResult.countFinished; + outResult.isFinished = true; + deferred.resolve(resolution); + }); + return deferred.promise; +} + +function get_exn(f) { + try { + f(); + return null; + } catch (ex) { + return ex; + } +} + +function do_check_exn(exn, constructor) { + do_check_neq(exn, null); + if (exn.name == constructor) { + do_check_eq(exn.constructor.name, constructor); + return; + } + do_print("Wrong error constructor"); + do_print(exn.constructor.name); + do_print(exn.stack); + do_check_true(false); +} |