summaryrefslogtreecommitdiffstats
path: root/toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js')
-rw-r--r--toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js194
1 files changed, 194 insertions, 0 deletions
diff --git a/toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js b/toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js
new file mode 100644
index 000000000..f1aebc3ad
--- /dev/null
+++ b/toolkit/components/asyncshutdown/tests/xpcshell/test_AsyncShutdown.js
@@ -0,0 +1,194 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* test_no_condition() {
+ for (let kind of ["phase", "barrier", "xpcom-barrier", "xpcom-barrier-unwrapped"]) {
+ do_print("Testing a barrier with no condition (" + kind + ")");
+ let lock = makeLock(kind);
+ yield lock.wait();
+ do_print("Barrier with no condition didn't lock");
+ }
+});
+
+add_task(function* test_phase_various_failures() {
+ for (let kind of ["phase", "barrier", "xpcom-barrier", "xpcom-barrier-unwrapped"]) {
+ do_print("Kind: " + kind);
+ // Testing with wrong arguments
+ let lock = makeLock(kind);
+
+ Assert.throws(() => lock.addBlocker(), /TypeError|NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS/);
+ Assert.throws(() => lock.addBlocker(null, true), /TypeError|NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS/);
+
+ if (kind != "xpcom-barrier") {
+ // xpcom-barrier actually expects a string in that position
+ Assert.throws(() => lock.addBlocker("Test 2", () => true, "not a function"), /TypeError/);
+ }
+
+ // Attempting to add a blocker after we are done waiting
+ yield lock.wait();
+ Assert.throws(() => lock.addBlocker("Test 3", () => true), /is finished/);
+ }
+});
+
+add_task(function* test_reentrant() {
+ do_print("Ensure that we can call addBlocker from within a blocker");
+
+ for (let kind of ["phase", "barrier", "xpcom-barrier", "xpcom-barrier-unwrapped"]) {
+ do_print("Kind: " + kind);
+ let lock = makeLock(kind);
+
+ let deferredOuter = PromiseUtils.defer();
+ let deferredInner = PromiseUtils.defer();
+ let deferredBlockInner = PromiseUtils.defer();
+
+ lock.addBlocker("Outer blocker", () => {
+ do_print("Entering outer blocker");
+ deferredOuter.resolve();
+ lock.addBlocker("Inner blocker", () => {
+ do_print("Entering inner blocker");
+ deferredInner.resolve();
+ return deferredBlockInner.promise;
+ });
+ });
+
+ // Note that phase-style locks spin the event loop and do not return from
+ // `lock.wait()` until after all blockers have been resolved. Therefore,
+ // to be able to test them, we need to dispatch the following steps to the
+ // event loop before calling `lock.wait()`, which we do by forcing
+ // a Promise.resolve().
+ //
+ let promiseSteps = Task.spawn(function* () {
+ yield Promise.resolve();
+
+ do_print("Waiting until we have entered the outer blocker");
+ yield deferredOuter.promise;
+
+ do_print("Waiting until we have entered the inner blocker");
+ yield deferredInner.promise;
+
+ do_print("Allowing the lock to resolve")
+ deferredBlockInner.resolve();
+ });
+
+ do_print("Starting wait");
+ yield lock.wait();
+
+ do_print("Waiting until all steps have been walked");
+ yield promiseSteps;
+ }
+});
+
+
+add_task(function* test_phase_removeBlocker() {
+ do_print("Testing that we can call removeBlocker before, during and after the call to wait()");
+
+ for (let kind of ["phase", "barrier", "xpcom-barrier", "xpcom-barrier-unwrapped"]) {
+
+ do_print("Switching to kind " + kind);
+ do_print("Attempt to add then remove a blocker before wait()");
+ let lock = makeLock(kind);
+ let blocker = () => {
+ do_print("This promise will never be resolved");
+ return Promise.defer().promise;
+ };
+
+ lock.addBlocker("Wait forever", blocker);
+ let do_remove_blocker = function(aLock, aBlocker, aShouldRemove) {
+ do_print("Attempting to remove blocker " + aBlocker + ", expecting result " + aShouldRemove);
+ if (kind == "xpcom-barrier") {
+ // The xpcom variant always returns `undefined`, so we can't
+ // check its result.
+ aLock.removeBlocker(aBlocker);
+ return;
+ }
+ do_check_eq(aLock.removeBlocker(aBlocker), aShouldRemove);
+ };
+ do_remove_blocker(lock, blocker, true);
+ do_remove_blocker(lock, blocker, false);
+ do_print("Attempt to remove non-registered blockers before wait()");
+ do_remove_blocker(lock, "foo", false);
+ do_remove_blocker(lock, null, false);
+ do_print("Waiting (should lift immediately)");
+ yield lock.wait();
+
+ do_print("Attempt to add a blocker then remove it during wait()");
+ lock = makeLock(kind);
+ let blockers = [
+ () => {
+ do_print("This blocker will self-destruct");
+ do_remove_blocker(lock, blockers[0], true);
+ return Promise.defer().promise;
+ },
+ () => {
+ do_print("This blocker will self-destruct twice");
+ do_remove_blocker(lock, blockers[1], true);
+ do_remove_blocker(lock, blockers[1], false);
+ return Promise.defer().promise;
+ },
+ () => {
+ do_print("Attempt to remove non-registered blockers during wait()");
+ do_remove_blocker(lock, "foo", false);
+ do_remove_blocker(lock, null, false);
+ }
+ ];
+ for (let i in blockers) {
+ lock.addBlocker("Wait forever again: " + i, blockers[i]);
+ }
+ do_print("Waiting (should lift very quickly)");
+ yield lock.wait();
+ do_remove_blocker(lock, blockers[0], false);
+
+
+ do_print("Attempt to remove a blocker after wait");
+ lock = makeLock(kind);
+ blocker = Promise.resolve.bind(Promise);
+ yield lock.wait();
+ do_remove_blocker(lock, blocker, false);
+
+ do_print("Attempt to remove non-registered blocker after wait()");
+ do_remove_blocker(lock, "foo", false);
+ do_remove_blocker(lock, null, false);
+ }
+
+});
+
+add_task(function* test_state() {
+ do_print("Testing information contained in `state`");
+
+ let BLOCKER_NAME = "test_state blocker " + Math.random();
+
+ // Set up the barrier. Note that we cannot test `barrier.state`
+ // immediately, as it initially contains "Not started"
+ let barrier = new AsyncShutdown.Barrier("test_filename");
+ let deferred = Promise.defer();
+ let {filename, lineNumber} = Components.stack;
+ barrier.client.addBlocker(BLOCKER_NAME,
+ function() {
+ return deferred.promise;
+ });
+
+ let promiseDone = barrier.wait();
+
+ // Now that we have called `wait()`, the state contains interesting things
+ let state = barrier.state[0];
+ do_print("State: " + JSON.stringify(barrier.state, null, "\t"));
+ Assert.equal(state.filename, filename);
+ Assert.equal(state.lineNumber, lineNumber + 1);
+ Assert.equal(state.name, BLOCKER_NAME);
+ Assert.ok(state.stack.some(x => x.includes("test_state")), "The stack contains the caller function's name");
+ Assert.ok(state.stack.some(x => x.includes(filename)), "The stack contains the calling file's name");
+
+ deferred.resolve();
+ yield promiseDone;
+});
+
+add_task(function*() {
+ Services.prefs.clearUserPref("toolkit.asyncshutdown.testing");
+});