|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 /** |
|
5 * This file tests the DeferredTask.jsm module. |
|
6 */ |
|
7 |
|
8 //////////////////////////////////////////////////////////////////////////////// |
|
9 /// Globals |
|
10 |
|
11 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; |
|
12 |
|
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
14 |
|
15 XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", |
|
16 "resource://gre/modules/DeferredTask.jsm"); |
|
17 XPCOMUtils.defineLazyModuleGetter(this, "Promise", |
|
18 "resource://gre/modules/Promise.jsm"); |
|
19 |
|
20 /** |
|
21 * Due to the nature of this module, most of the tests are time-dependent. All |
|
22 * the timeouts are designed to occur at multiples of this granularity value, |
|
23 * in milliseconds, that should be high enough to prevent intermittent failures, |
|
24 * but low enough to prevent an excessive overall test execution time. |
|
25 */ |
|
26 const T = 100; |
|
27 |
|
28 /** |
|
29 * Waits for the specified timeout before resolving the returned promise. |
|
30 */ |
|
31 function promiseTimeout(aTimeoutMs) |
|
32 { |
|
33 let deferred = Promise.defer(); |
|
34 do_timeout(aTimeoutMs, deferred.resolve); |
|
35 return deferred.promise; |
|
36 } |
|
37 |
|
38 function run_test() |
|
39 { |
|
40 run_next_test(); |
|
41 } |
|
42 |
|
43 //////////////////////////////////////////////////////////////////////////////// |
|
44 //// Tests |
|
45 |
|
46 /** |
|
47 * Creates a simple DeferredTask and executes it once. |
|
48 */ |
|
49 add_test(function test_arm_simple() |
|
50 { |
|
51 new DeferredTask(run_next_test, 10).arm(); |
|
52 }); |
|
53 |
|
54 /** |
|
55 * Checks that the delay set for the task is respected. |
|
56 */ |
|
57 add_test(function test_arm_delay_respected() |
|
58 { |
|
59 let executed1 = false; |
|
60 let executed2 = false; |
|
61 |
|
62 new DeferredTask(function () { |
|
63 executed1 = true; |
|
64 do_check_false(executed2); |
|
65 }, 1*T).arm(); |
|
66 |
|
67 new DeferredTask(function () { |
|
68 executed2 = true; |
|
69 do_check_true(executed1); |
|
70 run_next_test(); |
|
71 }, 2*T).arm(); |
|
72 }); |
|
73 |
|
74 /** |
|
75 * Checks that calling "arm" again does not introduce further delay. |
|
76 */ |
|
77 add_test(function test_arm_delay_notrestarted() |
|
78 { |
|
79 let executed = false; |
|
80 |
|
81 // Create a task that will run later. |
|
82 let deferredTask = new DeferredTask(() => { executed = true; }, 4*T); |
|
83 deferredTask.arm(); |
|
84 |
|
85 // Before the task starts, call "arm" again. |
|
86 do_timeout(2*T, () => deferredTask.arm()); |
|
87 |
|
88 // The "arm" call should not have introduced further delays. |
|
89 do_timeout(5*T, function () { |
|
90 do_check_true(executed); |
|
91 run_next_test(); |
|
92 }); |
|
93 }); |
|
94 |
|
95 /** |
|
96 * Checks that a task runs only once when armed multiple times synchronously. |
|
97 */ |
|
98 add_test(function test_arm_coalesced() |
|
99 { |
|
100 let executed = false; |
|
101 |
|
102 let deferredTask = new DeferredTask(function () { |
|
103 do_check_false(executed); |
|
104 executed = true; |
|
105 run_next_test(); |
|
106 }, 50); |
|
107 |
|
108 deferredTask.arm(); |
|
109 deferredTask.arm(); |
|
110 }); |
|
111 |
|
112 /** |
|
113 * Checks that a task runs only once when armed multiple times synchronously, |
|
114 * even when it has been created with a delay of zero milliseconds. |
|
115 */ |
|
116 add_test(function test_arm_coalesced_nodelay() |
|
117 { |
|
118 let executed = false; |
|
119 |
|
120 let deferredTask = new DeferredTask(function () { |
|
121 do_check_false(executed); |
|
122 executed = true; |
|
123 run_next_test(); |
|
124 }, 0); |
|
125 |
|
126 deferredTask.arm(); |
|
127 deferredTask.arm(); |
|
128 }); |
|
129 |
|
130 /** |
|
131 * Checks that a task can be armed again while running. |
|
132 */ |
|
133 add_test(function test_arm_recursive() |
|
134 { |
|
135 let executed = false; |
|
136 |
|
137 let deferredTask = new DeferredTask(function () { |
|
138 if (!executed) { |
|
139 executed = true; |
|
140 deferredTask.arm(); |
|
141 } else { |
|
142 run_next_test(); |
|
143 } |
|
144 }, 50); |
|
145 |
|
146 deferredTask.arm(); |
|
147 }); |
|
148 |
|
149 /** |
|
150 * Checks that calling "arm" while an asynchronous task is running waits until |
|
151 * the task is finished before restarting the delay. |
|
152 */ |
|
153 add_test(function test_arm_async() |
|
154 { |
|
155 let finishedExecution = false; |
|
156 let finishedExecutionAgain = false; |
|
157 |
|
158 // Create a task that will run later. |
|
159 let deferredTask = new DeferredTask(function () { |
|
160 yield promiseTimeout(4*T); |
|
161 if (!finishedExecution) { |
|
162 finishedExecution = true; |
|
163 } else if (!finishedExecutionAgain) { |
|
164 finishedExecutionAgain = true; |
|
165 } |
|
166 }, 2*T); |
|
167 deferredTask.arm(); |
|
168 |
|
169 // While the task is running, call "arm" again. This will result in a wait |
|
170 // of 2*T until the task finishes, then another 2*T for the normal task delay |
|
171 // specified on construction. |
|
172 do_timeout(4*T, function () { |
|
173 do_check_true(deferredTask.isRunning); |
|
174 do_check_false(finishedExecution); |
|
175 deferredTask.arm(); |
|
176 }); |
|
177 |
|
178 // This will fail in case the task was started without waiting 2*T after it |
|
179 // has finished. |
|
180 do_timeout(7*T, function () { |
|
181 do_check_false(deferredTask.isRunning); |
|
182 do_check_true(finishedExecution); |
|
183 }); |
|
184 |
|
185 // This is in the middle of the second execution. |
|
186 do_timeout(10*T, function () { |
|
187 do_check_true(deferredTask.isRunning); |
|
188 do_check_false(finishedExecutionAgain); |
|
189 }); |
|
190 |
|
191 // Wait enough time to verify that the task was executed as expected. |
|
192 do_timeout(13*T, function () { |
|
193 do_check_false(deferredTask.isRunning); |
|
194 do_check_true(finishedExecutionAgain); |
|
195 run_next_test(); |
|
196 }); |
|
197 }); |
|
198 |
|
199 /** |
|
200 * Checks that an armed task can be disarmed. |
|
201 */ |
|
202 add_test(function test_disarm() |
|
203 { |
|
204 // Create a task that will run later. |
|
205 let deferredTask = new DeferredTask(function () { |
|
206 do_throw("This task should not run."); |
|
207 }, 2*T); |
|
208 deferredTask.arm(); |
|
209 |
|
210 // Disable execution later, but before the task starts. |
|
211 do_timeout(1*T, () => deferredTask.disarm()); |
|
212 |
|
213 // Wait enough time to verify that the task did not run. |
|
214 do_timeout(3*T, run_next_test); |
|
215 }); |
|
216 |
|
217 /** |
|
218 * Checks that calling "disarm" allows the delay to be restarted. |
|
219 */ |
|
220 add_test(function test_disarm_delay_restarted() |
|
221 { |
|
222 let executed = false; |
|
223 |
|
224 let deferredTask = new DeferredTask(() => { executed = true; }, 4*T); |
|
225 deferredTask.arm(); |
|
226 |
|
227 do_timeout(2*T, function () { |
|
228 deferredTask.disarm(); |
|
229 deferredTask.arm(); |
|
230 }); |
|
231 |
|
232 do_timeout(5*T, function () { |
|
233 do_check_false(executed); |
|
234 }); |
|
235 |
|
236 do_timeout(7*T, function () { |
|
237 do_check_true(executed); |
|
238 run_next_test(); |
|
239 }); |
|
240 }); |
|
241 |
|
242 /** |
|
243 * Checks that calling "disarm" while an asynchronous task is running does not |
|
244 * prevent the task to finish. |
|
245 */ |
|
246 add_test(function test_disarm_async() |
|
247 { |
|
248 let finishedExecution = false; |
|
249 |
|
250 let deferredTask = new DeferredTask(function () { |
|
251 deferredTask.arm(); |
|
252 yield promiseTimeout(2*T); |
|
253 finishedExecution = true; |
|
254 }, 1*T); |
|
255 deferredTask.arm(); |
|
256 |
|
257 do_timeout(2*T, function () { |
|
258 do_check_true(deferredTask.isRunning); |
|
259 do_check_true(deferredTask.isArmed); |
|
260 do_check_false(finishedExecution); |
|
261 deferredTask.disarm(); |
|
262 }); |
|
263 |
|
264 do_timeout(4*T, function () { |
|
265 do_check_false(deferredTask.isRunning); |
|
266 do_check_false(deferredTask.isArmed); |
|
267 do_check_true(finishedExecution); |
|
268 run_next_test(); |
|
269 }); |
|
270 }); |
|
271 |
|
272 /** |
|
273 * Checks that calling "arm" immediately followed by "disarm" while an |
|
274 * asynchronous task is running does not cause it to run again. |
|
275 */ |
|
276 add_test(function test_disarm_immediate_async() |
|
277 { |
|
278 let executed = false; |
|
279 |
|
280 let deferredTask = new DeferredTask(function () { |
|
281 do_check_false(executed); |
|
282 executed = true; |
|
283 yield promiseTimeout(2*T); |
|
284 }, 1*T); |
|
285 deferredTask.arm(); |
|
286 |
|
287 do_timeout(2*T, function () { |
|
288 do_check_true(deferredTask.isRunning); |
|
289 do_check_false(deferredTask.isArmed); |
|
290 deferredTask.arm(); |
|
291 deferredTask.disarm(); |
|
292 }); |
|
293 |
|
294 do_timeout(4*T, function () { |
|
295 do_check_true(executed); |
|
296 do_check_false(deferredTask.isRunning); |
|
297 do_check_false(deferredTask.isArmed); |
|
298 run_next_test(); |
|
299 }); |
|
300 }); |
|
301 |
|
302 /** |
|
303 * Checks the isArmed and isRunning properties with a synchronous task. |
|
304 */ |
|
305 add_test(function test_isArmed_isRunning() |
|
306 { |
|
307 let deferredTask = new DeferredTask(function () { |
|
308 do_check_true(deferredTask.isRunning); |
|
309 do_check_false(deferredTask.isArmed); |
|
310 deferredTask.arm(); |
|
311 do_check_true(deferredTask.isArmed); |
|
312 deferredTask.disarm(); |
|
313 do_check_false(deferredTask.isArmed); |
|
314 run_next_test(); |
|
315 }, 50); |
|
316 |
|
317 do_check_false(deferredTask.isArmed); |
|
318 deferredTask.arm(); |
|
319 do_check_true(deferredTask.isArmed); |
|
320 do_check_false(deferredTask.isRunning); |
|
321 }); |
|
322 |
|
323 /** |
|
324 * Checks that the "finalize" method executes a synchronous task. |
|
325 */ |
|
326 add_test(function test_finalize() |
|
327 { |
|
328 let executed = false; |
|
329 let timePassed = false; |
|
330 |
|
331 let deferredTask = new DeferredTask(function () { |
|
332 do_check_false(timePassed); |
|
333 executed = true; |
|
334 }, 2*T); |
|
335 deferredTask.arm(); |
|
336 |
|
337 do_timeout(1*T, () => { timePassed = true; }); |
|
338 |
|
339 // This should trigger the immediate execution of the task. |
|
340 deferredTask.finalize().then(function () { |
|
341 do_check_true(executed); |
|
342 run_next_test(); |
|
343 }); |
|
344 }); |
|
345 |
|
346 /** |
|
347 * Checks that the "finalize" method executes the task again from start to |
|
348 * finish in case it is already running. |
|
349 */ |
|
350 add_test(function test_finalize_executes_entirely() |
|
351 { |
|
352 let executed = false; |
|
353 let executedAgain = false; |
|
354 let timePassed = false; |
|
355 |
|
356 let deferredTask = new DeferredTask(function () { |
|
357 // The first time, we arm the timer again and set up the finalization. |
|
358 if (!executed) { |
|
359 deferredTask.arm(); |
|
360 do_check_true(deferredTask.isArmed); |
|
361 do_check_true(deferredTask.isRunning); |
|
362 |
|
363 deferredTask.finalize().then(function () { |
|
364 // When we reach this point, the task must be finished. |
|
365 do_check_true(executedAgain); |
|
366 do_check_false(timePassed); |
|
367 do_check_false(deferredTask.isArmed); |
|
368 do_check_false(deferredTask.isRunning); |
|
369 run_next_test(); |
|
370 }); |
|
371 |
|
372 // The second execution triggered by the finalization waits 1*T for the |
|
373 // current task to finish (see the timeout below), but then it must not |
|
374 // wait for the 2*T specified on construction as normal task delay. The |
|
375 // second execution will finish after the timeout below has passed again, |
|
376 // for a total of 2*T of wait time. |
|
377 do_timeout(3*T, () => { timePassed = true; }); |
|
378 } |
|
379 |
|
380 yield promiseTimeout(1*T); |
|
381 |
|
382 // Just before finishing, indicate if we completed the second execution. |
|
383 if (executed) { |
|
384 do_check_true(deferredTask.isRunning); |
|
385 executedAgain = true; |
|
386 } else { |
|
387 executed = true; |
|
388 } |
|
389 }, 2*T); |
|
390 |
|
391 deferredTask.arm(); |
|
392 }); |