|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 /** |
|
5 * This file tests the Task.jsm module. |
|
6 */ |
|
7 |
|
8 //////////////////////////////////////////////////////////////////////////////// |
|
9 /// Globals |
|
10 |
|
11 const Cc = Components.classes; |
|
12 const Ci = Components.interfaces; |
|
13 const Cu = Components.utils; |
|
14 const Cr = Components.results; |
|
15 |
|
16 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
17 |
|
18 XPCOMUtils.defineLazyModuleGetter(this, "Promise", |
|
19 "resource://gre/modules/Promise.jsm"); |
|
20 XPCOMUtils.defineLazyModuleGetter(this, "Services", |
|
21 "resource://gre/modules/Services.jsm"); |
|
22 XPCOMUtils.defineLazyModuleGetter(this, "Task", |
|
23 "resource://gre/modules/Task.jsm"); |
|
24 |
|
25 /** |
|
26 * Returns a promise that will be resolved with the given value, when an event |
|
27 * posted on the event loop of the main thread is processed. |
|
28 */ |
|
29 function promiseResolvedLater(aValue) { |
|
30 let deferred = Promise.defer(); |
|
31 Services.tm.mainThread.dispatch(function () deferred.resolve(aValue), |
|
32 Ci.nsIThread.DISPATCH_NORMAL); |
|
33 return deferred.promise; |
|
34 } |
|
35 |
|
36 //////////////////////////////////////////////////////////////////////////////// |
|
37 /// Tests |
|
38 |
|
39 function run_test() |
|
40 { |
|
41 run_next_test(); |
|
42 } |
|
43 |
|
44 add_test(function test_normal() |
|
45 { |
|
46 Task.spawn(function () { |
|
47 let result = yield Promise.resolve("Value"); |
|
48 for (let i = 0; i < 3; i++) { |
|
49 result += yield promiseResolvedLater("!"); |
|
50 } |
|
51 throw new Task.Result("Task result: " + result); |
|
52 }).then(function (result) { |
|
53 do_check_eq("Task result: Value!!!", result); |
|
54 run_next_test(); |
|
55 }, function (ex) { |
|
56 do_throw("Unexpected error: " + ex); |
|
57 }); |
|
58 }); |
|
59 |
|
60 add_test(function test_exceptions() |
|
61 { |
|
62 Task.spawn(function () { |
|
63 try { |
|
64 yield Promise.reject("Rejection result by promise."); |
|
65 do_throw("Exception expected because the promise was rejected."); |
|
66 } catch (ex) { |
|
67 // We catch this exception now, we will throw a different one later. |
|
68 do_check_eq("Rejection result by promise.", ex); |
|
69 } |
|
70 throw new Error("Exception uncaught by task."); |
|
71 }).then(function (result) { |
|
72 do_throw("Unexpected success!"); |
|
73 }, function (ex) { |
|
74 do_check_eq("Exception uncaught by task.", ex.message); |
|
75 run_next_test(); |
|
76 }); |
|
77 }); |
|
78 |
|
79 add_test(function test_recursion() |
|
80 { |
|
81 function task_fibonacci(n) { |
|
82 throw new Task.Result(n < 2 ? n : (yield task_fibonacci(n - 1)) + |
|
83 (yield task_fibonacci(n - 2))); |
|
84 }; |
|
85 |
|
86 Task.spawn(task_fibonacci(6)).then(function (result) { |
|
87 do_check_eq(8, result); |
|
88 run_next_test(); |
|
89 }, function (ex) { |
|
90 do_throw("Unexpected error: " + ex); |
|
91 }); |
|
92 }); |
|
93 |
|
94 add_test(function test_spawn_primitive() |
|
95 { |
|
96 function fibonacci(n) { |
|
97 return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); |
|
98 }; |
|
99 |
|
100 // Polymorphism between task and non-task functions (see "test_recursion"). |
|
101 Task.spawn(fibonacci(6)).then(function (result) { |
|
102 do_check_eq(8, result); |
|
103 run_next_test(); |
|
104 }, function (ex) { |
|
105 do_throw("Unexpected error: " + ex); |
|
106 }); |
|
107 }); |
|
108 |
|
109 add_test(function test_spawn_function() |
|
110 { |
|
111 Task.spawn(function () { |
|
112 return "This is not a generator."; |
|
113 }).then(function (result) { |
|
114 do_check_eq("This is not a generator.", result); |
|
115 run_next_test(); |
|
116 }, function (ex) { |
|
117 do_throw("Unexpected error: " + ex); |
|
118 }); |
|
119 }); |
|
120 |
|
121 add_test(function test_spawn_function_this() |
|
122 { |
|
123 Task.spawn(function () { |
|
124 return this; |
|
125 }).then(function (result) { |
|
126 // Since the task function wasn't defined in strict mode, its "this" object |
|
127 // should be the same as the "this" object in this function, i.e. the global |
|
128 // object. |
|
129 do_check_eq(result, this); |
|
130 run_next_test(); |
|
131 }, function (ex) { |
|
132 do_throw("Unexpected error: " + ex); |
|
133 }); |
|
134 }); |
|
135 |
|
136 add_test(function test_spawn_function_this_strict() |
|
137 { |
|
138 "use strict"; |
|
139 Task.spawn(function () { |
|
140 return this; |
|
141 }).then(function (result) { |
|
142 // Since the task function was defined in strict mode, its "this" object |
|
143 // should be undefined. |
|
144 do_check_eq(typeof(result), "undefined"); |
|
145 run_next_test(); |
|
146 }, function (ex) { |
|
147 do_throw("Unexpected error: " + ex); |
|
148 }); |
|
149 }); |
|
150 |
|
151 add_test(function test_spawn_function_returning_promise() |
|
152 { |
|
153 Task.spawn(function () { |
|
154 return promiseResolvedLater("Resolution value."); |
|
155 }).then(function (result) { |
|
156 do_check_eq("Resolution value.", result); |
|
157 run_next_test(); |
|
158 }, function (ex) { |
|
159 do_throw("Unexpected error: " + ex); |
|
160 }); |
|
161 }); |
|
162 |
|
163 add_test(function test_spawn_function_exceptions() |
|
164 { |
|
165 Task.spawn(function () { |
|
166 throw new Error("Exception uncaught by task."); |
|
167 }).then(function (result) { |
|
168 do_throw("Unexpected success!"); |
|
169 }, function (ex) { |
|
170 do_check_eq("Exception uncaught by task.", ex.message); |
|
171 run_next_test(); |
|
172 }); |
|
173 }); |
|
174 |
|
175 add_test(function test_spawn_function_taskresult() |
|
176 { |
|
177 Task.spawn(function () { |
|
178 throw new Task.Result("Task result"); |
|
179 }).then(function (result) { |
|
180 do_check_eq("Task result", result); |
|
181 run_next_test(); |
|
182 }, function (ex) { |
|
183 do_throw("Unexpected error: " + ex); |
|
184 }); |
|
185 }); |
|
186 |
|
187 add_test(function test_yielded_undefined() |
|
188 { |
|
189 Task.spawn(function () { |
|
190 yield; |
|
191 throw new Task.Result("We continued correctly."); |
|
192 }).then(function (result) { |
|
193 do_check_eq("We continued correctly.", result); |
|
194 run_next_test(); |
|
195 }, function (ex) { |
|
196 do_throw("Unexpected error: " + ex); |
|
197 }); |
|
198 }); |
|
199 |
|
200 add_test(function test_yielded_primitive() |
|
201 { |
|
202 Task.spawn(function () { |
|
203 throw new Task.Result("Primitive " + (yield "value.")); |
|
204 }).then(function (result) { |
|
205 do_check_eq("Primitive value.", result); |
|
206 run_next_test(); |
|
207 }, function (ex) { |
|
208 do_throw("Unexpected error: " + ex); |
|
209 }); |
|
210 }); |
|
211 |
|
212 add_test(function test_star_normal() |
|
213 { |
|
214 Task.spawn(function* () { |
|
215 let result = yield Promise.resolve("Value"); |
|
216 for (let i = 0; i < 3; i++) { |
|
217 result += yield promiseResolvedLater("!"); |
|
218 } |
|
219 return "Task result: " + result; |
|
220 }).then(function (result) { |
|
221 do_check_eq("Task result: Value!!!", result); |
|
222 run_next_test(); |
|
223 }, function (ex) { |
|
224 do_throw("Unexpected error: " + ex); |
|
225 }); |
|
226 }); |
|
227 |
|
228 add_test(function test_star_exceptions() |
|
229 { |
|
230 Task.spawn(function* () { |
|
231 try { |
|
232 yield Promise.reject("Rejection result by promise."); |
|
233 do_throw("Exception expected because the promise was rejected."); |
|
234 } catch (ex) { |
|
235 // We catch this exception now, we will throw a different one later. |
|
236 do_check_eq("Rejection result by promise.", ex); |
|
237 } |
|
238 throw new Error("Exception uncaught by task."); |
|
239 }).then(function (result) { |
|
240 do_throw("Unexpected success!"); |
|
241 }, function (ex) { |
|
242 do_check_eq("Exception uncaught by task.", ex.message); |
|
243 run_next_test(); |
|
244 }); |
|
245 }); |
|
246 |
|
247 add_test(function test_star_recursion() |
|
248 { |
|
249 function* task_fibonacci(n) { |
|
250 return n < 2 ? n : (yield task_fibonacci(n - 1)) + |
|
251 (yield task_fibonacci(n - 2)); |
|
252 }; |
|
253 |
|
254 Task.spawn(task_fibonacci(6)).then(function (result) { |
|
255 do_check_eq(8, result); |
|
256 run_next_test(); |
|
257 }, function (ex) { |
|
258 do_throw("Unexpected error: " + ex); |
|
259 }); |
|
260 }); |
|
261 |
|
262 add_test(function test_mixed_legacy_and_star() |
|
263 { |
|
264 Task.spawn(function* () { |
|
265 return yield (function() { |
|
266 throw new Task.Result(yield 5); |
|
267 })(); |
|
268 }).then(function (result) { |
|
269 do_check_eq(5, result); |
|
270 run_next_test(); |
|
271 }, function (ex) { |
|
272 do_throw("Unexpected error: " + ex); |
|
273 }); |
|
274 }); |
|
275 |
|
276 add_test(function test_async_function_from_generator() |
|
277 { |
|
278 Task.spawn(function* () { |
|
279 let object = { |
|
280 asyncFunction: Task.async(function* (param) { |
|
281 do_check_eq(this, object); |
|
282 return param; |
|
283 }) |
|
284 }; |
|
285 |
|
286 // Ensure the async function returns a promise that resolves as expected. |
|
287 do_check_eq((yield object.asyncFunction(1)), 1); |
|
288 |
|
289 // Ensure a second call to the async function also returns such a promise. |
|
290 do_check_eq((yield object.asyncFunction(3)), 3); |
|
291 }).then(function () { |
|
292 run_next_test(); |
|
293 }, function (ex) { |
|
294 do_throw("Unexpected error: " + ex); |
|
295 }); |
|
296 }); |
|
297 |
|
298 add_test(function test_async_function_from_function() |
|
299 { |
|
300 Task.spawn(function* () { |
|
301 return Task.spawn(function* () { |
|
302 let object = { |
|
303 asyncFunction: Task.async(function (param) { |
|
304 do_check_eq(this, object); |
|
305 return param; |
|
306 }) |
|
307 }; |
|
308 |
|
309 // Ensure the async function returns a promise that resolves as expected. |
|
310 do_check_eq((yield object.asyncFunction(5)), 5); |
|
311 |
|
312 // Ensure a second call to the async function also returns such a promise. |
|
313 do_check_eq((yield object.asyncFunction(7)), 7); |
|
314 }); |
|
315 }).then(function () { |
|
316 run_next_test(); |
|
317 }, function (ex) { |
|
318 do_throw("Unexpected error: " + ex); |
|
319 }); |
|
320 }); |
|
321 |
|
322 add_test(function test_async_function_that_throws_rejects_promise() |
|
323 { |
|
324 Task.spawn(function* () { |
|
325 let object = { |
|
326 asyncFunction: Task.async(function* () { |
|
327 throw "Rejected!"; |
|
328 }) |
|
329 }; |
|
330 |
|
331 yield object.asyncFunction(); |
|
332 }).then(function () { |
|
333 do_throw("unexpected success calling async function that throws error"); |
|
334 }, function (ex) { |
|
335 do_check_eq(ex, "Rejected!"); |
|
336 run_next_test(); |
|
337 }); |
|
338 }); |
|
339 |
|
340 add_test(function test_async_return_function() |
|
341 { |
|
342 Task.spawn(function* () { |
|
343 // Ensure an async function that returns a function resolves to the function |
|
344 // itself instead of calling the function and resolving to its return value. |
|
345 return Task.spawn(function* () { |
|
346 let returnValue = function () { |
|
347 return "These aren't the droids you're looking for."; |
|
348 }; |
|
349 |
|
350 let asyncFunction = Task.async(function () { |
|
351 return returnValue; |
|
352 }); |
|
353 |
|
354 do_check_eq((yield asyncFunction()), returnValue); |
|
355 }); |
|
356 }).then(function () { |
|
357 run_next_test(); |
|
358 }, function (ex) { |
|
359 do_throw("Unexpected error: " + ex); |
|
360 }); |
|
361 }); |
|
362 |
|
363 add_test(function test_async_throw_argument_not_function() |
|
364 { |
|
365 Task.spawn(function* () { |
|
366 // Ensure Task.async throws if its aTask argument is not a function. |
|
367 Assert.throws(() => Task.async("not a function"), |
|
368 /aTask argument must be a function/); |
|
369 }).then(function () { |
|
370 run_next_test(); |
|
371 }, function (ex) { |
|
372 do_throw("Unexpected error: " + ex); |
|
373 }); |
|
374 }); |
|
375 |
|
376 add_test(function test_async_throw_on_function_in_place_of_promise() |
|
377 { |
|
378 Task.spawn(function* () { |
|
379 // Ensure Task.spawn throws if passed an async function. |
|
380 Assert.throws(() => Task.spawn(Task.async(function* () {})), |
|
381 /Cannot use an async function in place of a promise/); |
|
382 }).then(function () { |
|
383 run_next_test(); |
|
384 }, function (ex) { |
|
385 do_throw("Unexpected error: " + ex); |
|
386 }); |
|
387 }); |