|
1 let Cu = Components.utils; |
|
2 |
|
3 Cu.import("resource://gre/modules/Services.jsm"); |
|
4 Cu.import("resource://gre/modules/Promise.jsm"); |
|
5 Cu.import("resource://gre/modules/AsyncShutdown.jsm"); |
|
6 |
|
7 Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true); |
|
8 |
|
9 /** |
|
10 * An asynchronous task that takes several ticks to complete. |
|
11 * |
|
12 * @param {*=} resolution The value with which the resulting promise will be |
|
13 * resolved once the task is complete. This may be a rejected promise, |
|
14 * in which case the resulting promise will itself be rejected. |
|
15 * @param {object=} outResult An object modified by side-effect during the task. |
|
16 * Initially, its field |isFinished| is set to |false|. Once the task is |
|
17 * complete, its field |isFinished| is set to |true|. |
|
18 * |
|
19 * @return {promise} A promise fulfilled once the task is complete |
|
20 */ |
|
21 function longRunningAsyncTask(resolution = undefined, outResult = {}) { |
|
22 outResult.isFinished = false; |
|
23 if (!("countFinished" in outResult)) { |
|
24 outResult.countFinished = 0; |
|
25 } |
|
26 let deferred = Promise.defer(); |
|
27 do_timeout(100, function() { |
|
28 ++outResult.countFinished; |
|
29 outResult.isFinished = true; |
|
30 deferred.resolve(resolution); |
|
31 }); |
|
32 return deferred.promise; |
|
33 } |
|
34 |
|
35 /** |
|
36 * Generate a unique notification topic. |
|
37 */ |
|
38 function getUniqueTopic() { |
|
39 const PREFIX = "testing-phases-"; |
|
40 return PREFIX + ++getUniqueTopic.counter; |
|
41 } |
|
42 getUniqueTopic.counter = 0; |
|
43 |
|
44 add_task(function test_no_condition() { |
|
45 do_print("Testing a phase with no condition"); |
|
46 let topic = getUniqueTopic(); |
|
47 AsyncShutdown._getPhase(topic); |
|
48 Services.obs.notifyObservers(null, topic, null); |
|
49 do_print("Phase with no condition didn't lock"); |
|
50 }); |
|
51 |
|
52 add_task(function test_simple_async() { |
|
53 do_print("Testing various combinations of a phase with a single condition"); |
|
54 for (let arg of [undefined, null, "foo", 100, new Error("BOOM")]) { |
|
55 for (let resolution of [arg, Promise.reject(arg)]) { |
|
56 for (let success of [false, true]) { |
|
57 for (let state of [[null], |
|
58 [], |
|
59 [() => "some state"], |
|
60 [function() { |
|
61 throw new Error("State BOOM"); }], |
|
62 [function() { |
|
63 return { |
|
64 toJSON: function() { |
|
65 throw new Error("State.toJSON BOOM"); |
|
66 } |
|
67 }; |
|
68 }]]) { |
|
69 // Asynchronous phase |
|
70 do_print("Asynchronous test with " + arg + ", " + resolution); |
|
71 let topic = getUniqueTopic(); |
|
72 let outParam = { isFinished: false }; |
|
73 AsyncShutdown._getPhase(topic).addBlocker( |
|
74 "Async test", |
|
75 function() { |
|
76 if (success) { |
|
77 return longRunningAsyncTask(resolution, outParam); |
|
78 } else { |
|
79 throw resolution; |
|
80 } |
|
81 }, |
|
82 ...state |
|
83 ); |
|
84 do_check_false(outParam.isFinished); |
|
85 Services.obs.notifyObservers(null, topic, null); |
|
86 do_check_eq(outParam.isFinished, success); |
|
87 } |
|
88 } |
|
89 |
|
90 // Synchronous phase - just test that we don't throw/freeze |
|
91 do_print("Synchronous test with " + arg + ", " + resolution); |
|
92 let topic = getUniqueTopic(); |
|
93 AsyncShutdown._getPhase(topic).addBlocker( |
|
94 "Sync test", |
|
95 resolution |
|
96 ); |
|
97 Services.obs.notifyObservers(null, topic, null); |
|
98 } |
|
99 } |
|
100 }); |
|
101 |
|
102 add_task(function test_many() { |
|
103 do_print("Testing various combinations of a phase with many conditions"); |
|
104 let topic = getUniqueTopic(); |
|
105 let phase = AsyncShutdown._getPhase(topic); |
|
106 let outParams = []; |
|
107 for (let arg of [undefined, null, "foo", 100, new Error("BOOM")]) { |
|
108 for (let resolution of [arg, Promise.reject(arg)]) { |
|
109 let outParam = { isFinished: false }; |
|
110 phase.addBlocker( |
|
111 "Test", |
|
112 () => longRunningAsyncTask(resolution, outParam) |
|
113 ); |
|
114 } |
|
115 } |
|
116 do_check_true(outParams.every((x) => !x.isFinished)); |
|
117 Services.obs.notifyObservers(null, topic, null); |
|
118 do_check_true(outParams.every((x) => x.isFinished)); |
|
119 }); |
|
120 |
|
121 function get_exn(f) { |
|
122 try { |
|
123 f(); |
|
124 return null; |
|
125 } catch (ex) { |
|
126 return ex; |
|
127 } |
|
128 } |
|
129 |
|
130 add_task(function test_various_failures() { |
|
131 do_print("Ensure that we cannot add a condition for a phase that is already complete"); |
|
132 let topic = getUniqueTopic(); |
|
133 let phase = AsyncShutdown._getPhase(topic); |
|
134 Services.obs.notifyObservers(null, topic, null); |
|
135 let exn = get_exn(() => phase.addBlocker("Test", true)); |
|
136 do_check_true(!!exn); |
|
137 |
|
138 do_print("Ensure that an incomplete blocker causes a TypeError"); |
|
139 |
|
140 exn = get_exn(() => phase.addBlocker()); |
|
141 do_check_eq(exn.name, "TypeError"); |
|
142 |
|
143 exn = get_exn(() => phase.addBlocker(null, true)); |
|
144 do_check_eq(exn.name, "TypeError"); |
|
145 |
|
146 exn = get_exn(() => phase.addBlocker("Test 2", () => true, "not a function")); |
|
147 do_check_eq(exn.name, "TypeError"); |
|
148 }); |
|
149 |
|
150 add_task(function() { |
|
151 Services.prefs.clearUserPref("toolkit.asyncshutdown.testing"); |
|
152 }); |
|
153 |
|
154 function run_test() { |
|
155 run_next_test(); |
|
156 } |