1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/modules/tests/xpcshell/test_AsyncShutdown.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,156 @@ 1.4 +let Cu = Components.utils; 1.5 + 1.6 +Cu.import("resource://gre/modules/Services.jsm"); 1.7 +Cu.import("resource://gre/modules/Promise.jsm"); 1.8 +Cu.import("resource://gre/modules/AsyncShutdown.jsm"); 1.9 + 1.10 +Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true); 1.11 + 1.12 +/** 1.13 + * An asynchronous task that takes several ticks to complete. 1.14 + * 1.15 + * @param {*=} resolution The value with which the resulting promise will be 1.16 + * resolved once the task is complete. This may be a rejected promise, 1.17 + * in which case the resulting promise will itself be rejected. 1.18 + * @param {object=} outResult An object modified by side-effect during the task. 1.19 + * Initially, its field |isFinished| is set to |false|. Once the task is 1.20 + * complete, its field |isFinished| is set to |true|. 1.21 + * 1.22 + * @return {promise} A promise fulfilled once the task is complete 1.23 + */ 1.24 +function longRunningAsyncTask(resolution = undefined, outResult = {}) { 1.25 + outResult.isFinished = false; 1.26 + if (!("countFinished" in outResult)) { 1.27 + outResult.countFinished = 0; 1.28 + } 1.29 + let deferred = Promise.defer(); 1.30 + do_timeout(100, function() { 1.31 + ++outResult.countFinished; 1.32 + outResult.isFinished = true; 1.33 + deferred.resolve(resolution); 1.34 + }); 1.35 + return deferred.promise; 1.36 +} 1.37 + 1.38 +/** 1.39 + * Generate a unique notification topic. 1.40 + */ 1.41 +function getUniqueTopic() { 1.42 + const PREFIX = "testing-phases-"; 1.43 + return PREFIX + ++getUniqueTopic.counter; 1.44 +} 1.45 +getUniqueTopic.counter = 0; 1.46 + 1.47 +add_task(function test_no_condition() { 1.48 + do_print("Testing a phase with no condition"); 1.49 + let topic = getUniqueTopic(); 1.50 + AsyncShutdown._getPhase(topic); 1.51 + Services.obs.notifyObservers(null, topic, null); 1.52 + do_print("Phase with no condition didn't lock"); 1.53 +}); 1.54 + 1.55 +add_task(function test_simple_async() { 1.56 + do_print("Testing various combinations of a phase with a single condition"); 1.57 + for (let arg of [undefined, null, "foo", 100, new Error("BOOM")]) { 1.58 + for (let resolution of [arg, Promise.reject(arg)]) { 1.59 + for (let success of [false, true]) { 1.60 + for (let state of [[null], 1.61 + [], 1.62 + [() => "some state"], 1.63 + [function() { 1.64 + throw new Error("State BOOM"); }], 1.65 + [function() { 1.66 + return { 1.67 + toJSON: function() { 1.68 + throw new Error("State.toJSON BOOM"); 1.69 + } 1.70 + }; 1.71 + }]]) { 1.72 + // Asynchronous phase 1.73 + do_print("Asynchronous test with " + arg + ", " + resolution); 1.74 + let topic = getUniqueTopic(); 1.75 + let outParam = { isFinished: false }; 1.76 + AsyncShutdown._getPhase(topic).addBlocker( 1.77 + "Async test", 1.78 + function() { 1.79 + if (success) { 1.80 + return longRunningAsyncTask(resolution, outParam); 1.81 + } else { 1.82 + throw resolution; 1.83 + } 1.84 + }, 1.85 + ...state 1.86 + ); 1.87 + do_check_false(outParam.isFinished); 1.88 + Services.obs.notifyObservers(null, topic, null); 1.89 + do_check_eq(outParam.isFinished, success); 1.90 + } 1.91 + } 1.92 + 1.93 + // Synchronous phase - just test that we don't throw/freeze 1.94 + do_print("Synchronous test with " + arg + ", " + resolution); 1.95 + let topic = getUniqueTopic(); 1.96 + AsyncShutdown._getPhase(topic).addBlocker( 1.97 + "Sync test", 1.98 + resolution 1.99 + ); 1.100 + Services.obs.notifyObservers(null, topic, null); 1.101 + } 1.102 + } 1.103 +}); 1.104 + 1.105 +add_task(function test_many() { 1.106 + do_print("Testing various combinations of a phase with many conditions"); 1.107 + let topic = getUniqueTopic(); 1.108 + let phase = AsyncShutdown._getPhase(topic); 1.109 + let outParams = []; 1.110 + for (let arg of [undefined, null, "foo", 100, new Error("BOOM")]) { 1.111 + for (let resolution of [arg, Promise.reject(arg)]) { 1.112 + let outParam = { isFinished: false }; 1.113 + phase.addBlocker( 1.114 + "Test", 1.115 + () => longRunningAsyncTask(resolution, outParam) 1.116 + ); 1.117 + } 1.118 + } 1.119 + do_check_true(outParams.every((x) => !x.isFinished)); 1.120 + Services.obs.notifyObservers(null, topic, null); 1.121 + do_check_true(outParams.every((x) => x.isFinished)); 1.122 +}); 1.123 + 1.124 +function get_exn(f) { 1.125 + try { 1.126 + f(); 1.127 + return null; 1.128 + } catch (ex) { 1.129 + return ex; 1.130 + } 1.131 +} 1.132 + 1.133 +add_task(function test_various_failures() { 1.134 + do_print("Ensure that we cannot add a condition for a phase that is already complete"); 1.135 + let topic = getUniqueTopic(); 1.136 + let phase = AsyncShutdown._getPhase(topic); 1.137 + Services.obs.notifyObservers(null, topic, null); 1.138 + let exn = get_exn(() => phase.addBlocker("Test", true)); 1.139 + do_check_true(!!exn); 1.140 + 1.141 + do_print("Ensure that an incomplete blocker causes a TypeError"); 1.142 + 1.143 + exn = get_exn(() => phase.addBlocker()); 1.144 + do_check_eq(exn.name, "TypeError"); 1.145 + 1.146 + exn = get_exn(() => phase.addBlocker(null, true)); 1.147 + do_check_eq(exn.name, "TypeError"); 1.148 + 1.149 + exn = get_exn(() => phase.addBlocker("Test 2", () => true, "not a function")); 1.150 + do_check_eq(exn.name, "TypeError"); 1.151 +}); 1.152 + 1.153 +add_task(function() { 1.154 + Services.prefs.clearUserPref("toolkit.asyncshutdown.testing"); 1.155 +}); 1.156 + 1.157 +function run_test() { 1.158 + run_next_test(); 1.159 +}