src: disable fast math on arm
[platform/upstream/nodejs.git] / test / parallel / test-promises-unhandled-rejections.js
1 var common = require('../common');
2 var assert = require('assert');
3 var domain = require('domain');
4
5 var asyncTest = (function() {
6   var asyncTestsEnabled = false;
7   var asyncTestLastCheck;
8   var asyncTestQueue = [];
9   var asyncTestHandle;
10   var currentTest = null;
11
12   function fail(error) {
13     var stack = currentTest
14           ? error.stack + '\nFrom previous event:\n' + currentTest.stack
15           : error.stack;
16
17     if (currentTest)
18       process.stderr.write('\'' + currentTest.description + '\' failed\n\n');
19
20     process.stderr.write(stack);
21     process.exit(2);
22   }
23
24   function nextAsyncTest() {
25     var called = false;
26     function done(err) {
27       if (called) return fail(new Error('done called twice'));
28       called = true;
29       asyncTestLastCheck = Date.now();
30       if (arguments.length > 0) return fail(err);
31       setTimeout(nextAsyncTest, 10);
32     }
33
34     if (asyncTestQueue.length) {
35       var test = asyncTestQueue.shift();
36       currentTest = test;
37       test.action(done);
38     } else {
39       clearInterval(asyncTestHandle);
40     }
41   }
42
43   return function asyncTest(description, fn) {
44     var stack = new Error().stack.split('\n').slice(1).join('\n');
45     asyncTestQueue.push({
46       action: fn,
47       stack: stack,
48       description: description
49     });
50     if (!asyncTestsEnabled) {
51       asyncTestsEnabled = true;
52       asyncTestLastCheck = Date.now();
53       process.on('uncaughtException', fail);
54       asyncTestHandle = setInterval(function() {
55         var now = Date.now();
56         if (now - asyncTestLastCheck > 10000) {
57           return fail(new Error('Async test timeout exceeded'));
58         }
59       }, 10);
60       setTimeout(nextAsyncTest, 10);
61     }
62   };
63
64 })();
65
66 function setupException(fn) {
67   var listeners = process.listeners('uncaughtException');
68   process.removeAllListeners('uncaughtException');
69   process.on('uncaughtException', fn);
70   return function clean() {
71     process.removeListener('uncaughtException', fn);
72     listeners.forEach(function(listener) {
73       process.on('uncaughtException', listener);
74     });
75   };
76 }
77
78 function clean() {
79   process.removeAllListeners('unhandledRejection');
80   process.removeAllListeners('rejectionHandled');
81 }
82
83 function onUnhandledSucceed(done, predicate) {
84   clean();
85   process.on('unhandledRejection', function(reason, promise) {
86     try {
87       predicate(reason, promise);
88     } catch (e) {
89       return done(e);
90     }
91     done();
92   });
93 }
94
95 function onUnhandledFail(done) {
96   clean();
97   process.on('unhandledRejection', function(reason, promise) {
98     done(new Error('unhandledRejection not supposed to be triggered'));
99   });
100   process.on('rejectionHandled', function() {
101     done(new Error('rejectionHandled not supposed to be triggered'));
102   });
103   setTimeout(function() {
104     done();
105   }, 10);
106 }
107
108 asyncTest('synchronously rejected promise should trigger unhandledRejection', function(done) {
109   var e = new Error();
110   onUnhandledSucceed(done, function(reason, promise) {
111     assert.strictEqual(e, reason);
112   });
113   Promise.reject(e);
114 });
115
116 asyncTest('synchronously rejected promise should trigger unhandledRejection', function(done) {
117   var e = new Error();
118   onUnhandledSucceed(done, function(reason, promise) {
119     assert.strictEqual(e, reason);
120   });
121   new Promise(function(_, reject) {
122     reject(e);
123   });
124 });
125
126 asyncTest('Promise rejected after setImmediate should trigger unhandledRejection', function(done) {
127   var e = new Error();
128   onUnhandledSucceed(done, function(reason, promise) {
129     assert.strictEqual(e, reason);
130   });
131   new Promise(function(_, reject) {
132     setImmediate(function() {
133       reject(e);
134     });
135   });
136 });
137
138 asyncTest('Promise rejected after setTimeout(,1) should trigger unhandled rejection', function(done) {
139   var e = new Error();
140   onUnhandledSucceed(done, function(reason, promise) {
141     assert.strictEqual(e, reason);
142   });
143   new Promise(function(_, reject) {
144     setTimeout(function() {
145       reject(e);
146     }, 1);
147   });
148 });
149
150 asyncTest('Catching a promise rejection after setImmediate is not soon enough to stop unhandledRejection', function(done) {
151   var e = new Error();
152   onUnhandledSucceed(done, function(reason, promise) {
153     assert.strictEqual(e, reason);
154   });
155   var _reject;
156   var promise = new Promise(function(_, reject) {
157     _reject = reject;
158   })
159   _reject(e);
160   setImmediate(function() {
161     promise.then(assert.fail, function(){});
162   });
163 });
164
165 asyncTest('When re-throwing new errors in a promise catch, only the re-thrown error should hit unhandledRejection', function(done) {
166   var e = new Error();
167   var e2 = new Error();
168   onUnhandledSucceed(done, function(reason, promise) {
169     assert.strictEqual(e2, reason);
170     assert.strictEqual(promise2, promise);
171   });
172   var promise2 = Promise.reject(e).then(assert.fail, function(reason) {
173     assert.strictEqual(e, reason);
174     throw e2;
175   });
176 });
177
178 asyncTest('Test params of unhandledRejection for a synchronously-rejected promise', function(done) {
179   var e = new Error();
180   var e2 = new Error();
181   onUnhandledSucceed(done, function(reason, promise) {
182     assert.strictEqual(e, reason);
183     assert.strictEqual(promise, promise);
184   });
185   var promise = Promise.reject(e);
186 });
187
188 asyncTest('When re-throwing new errors in a promise catch, only the re-thrown error should hit unhandledRejection: original promise rejected async with setTimeout(,1)', function(done) {
189   var e = new Error();
190   var e2 = new Error();
191   onUnhandledSucceed(done, function(reason, promise) {
192     assert.strictEqual(e2, reason);
193     assert.strictEqual(promise2, promise);
194   });
195   var promise2 = new Promise(function(_, reject) {
196     setTimeout(function() {
197       reject(e);
198     }, 1);
199   }).then(assert.fail, function(reason) {
200     assert.strictEqual(e, reason);
201     throw e2;
202   });
203 });
204
205 asyncTest('When re-throwing new errors in a promise catch, only the re-thrown error should hit unhandledRejection: promise catch attached a process.nextTick after rejection', function(done) {
206   var e = new Error();
207   var e2 = new Error();
208   onUnhandledSucceed(done, function(reason, promise) {
209     assert.strictEqual(e2, reason);
210     assert.strictEqual(promise2, promise);
211   });
212   var promise = new Promise(function(_, reject) {
213     setTimeout(function() {
214       reject(e);
215       process.nextTick(function() {
216         promise2 = promise.then(assert.fail, function(reason) {
217           assert.strictEqual(e, reason);
218           throw e2;
219         });
220       });
221     }, 1);
222   });
223   var promise2;
224 });
225
226 asyncTest('unhandledRejection should not be triggered if a promise catch is attached synchronously upon the promise\'s creation', function(done) {
227   var e = new Error();
228   onUnhandledFail(done);
229   Promise.reject(e).then(assert.fail, function(){});
230 });
231
232 asyncTest('unhandledRejection should not be triggered if a promise catch is attached synchronously upon the promise\'s creation', function(done) {
233   var e = new Error();
234   onUnhandledFail(done);
235   new Promise(function(_, reject) {
236     reject(e);
237   }).then(assert.fail, function(){});
238 });
239
240 asyncTest('Attaching a promise catch in a process.nextTick is soon enough to prevent unhandledRejection', function(done) {
241   var e = new Error();
242   onUnhandledFail(done);
243   var promise = Promise.reject(e);
244   process.nextTick(function() {
245     promise.then(assert.fail, function(){});
246   });
247 });
248
249 asyncTest('Attaching a promise catch in a process.nextTick is soon enough to prevent unhandledRejection', function(done) {
250   var e = new Error();
251   onUnhandledFail(done);
252   var promise = new Promise(function(_, reject) {
253     reject(e);
254   });
255   process.nextTick(function() {
256     promise.then(assert.fail, function(){});
257   });
258 });
259
260 // State adapation tests
261 asyncTest('catching a promise which is asynchronously rejected (via resolution to an asynchronously-rejected promise) prevents unhandledRejection', function(done) {
262   var e = new Error();
263   onUnhandledFail(done);
264   Promise.resolve().then(function() {
265     return new Promise(function(_, reject) {
266       setTimeout(function() {
267         reject(e);
268       }, 1);
269     });
270   }).then(assert.fail, function(reason) {
271     assert.strictEqual(e, reason);
272   });
273 });
274
275 asyncTest('Catching a rejected promise derived from throwing in a fulfillment handler prevents unhandledRejection', function(done) {
276   var e = new Error();
277   onUnhandledFail(done);
278   Promise.resolve().then(function() {
279     throw e;
280   }).then(assert.fail, function(reason) {
281     assert.strictEqual(e, reason);
282   });
283 });
284
285 asyncTest('Catching a rejected promise derived from returning a synchronously-rejected promise in a fulfillment handler prevents unhandledRejection', function(done) {
286   var e = new Error();
287   onUnhandledFail(done);
288   Promise.resolve().then(function() {
289     return Promise.reject(e);
290   }).then(assert.fail, function(reason) {
291     assert.strictEqual(e, reason);
292   });
293 });
294
295 asyncTest('A rejected promise derived from returning an asynchronously-rejected promise in a fulfillment handler does trigger unhandledRejection', function(done) {
296   var e = new Error();
297   var _promise;
298   onUnhandledSucceed(done, function(reason, promise) {
299     assert.strictEqual(e, reason);
300     assert.strictEqual(_promise, promise);
301   });
302   _promise = Promise.resolve().then(function() {
303     return new Promise(function(_, reject) {
304       setTimeout(function() {
305         reject(e);
306       }, 1);
307     });
308   });
309 });
310
311 asyncTest('A rejected promise derived from throwing in a fulfillment handler does trigger unhandledRejection', function(done) {
312   var e = new Error();
313   var _promise;
314   onUnhandledSucceed(done, function(reason, promise) {
315     assert.strictEqual(e, reason);
316     assert.strictEqual(_promise, promise);
317   });
318   _promise = Promise.resolve().then(function() {
319     throw e;
320   });
321 });
322
323 asyncTest('A rejected promise derived from returning a synchronously-rejected promise in a fulfillment handler does trigger unhandledRejection', function(done) {
324   var e = new Error();
325   var _promise;
326   onUnhandledSucceed(done, function(reason, promise) {
327     assert.strictEqual(e, reason);
328     assert.strictEqual(_promise, promise);
329   });
330   _promise = Promise.resolve().then(function() {
331     return Promise.reject(e);
332   });
333 });
334
335 // Combinations with Promise.all
336 asyncTest('Catching the Promise.all() of a collection that includes a rejected promise prevents unhandledRejection', function(done) {
337   var e = new Error();
338   onUnhandledFail(done);
339   Promise.all([Promise.reject(e)]).then(assert.fail, function() {});
340 });
341
342 asyncTest('Catching the Promise.all() of a collection that includes a nextTick-async rejected promise prevents unhandledRejection', function(done) {
343   var e = new Error();
344   onUnhandledFail(done);
345   var p = new Promise(function(_, reject) {
346     process.nextTick(function() {
347       reject(e);
348     });
349   });
350   p = Promise.all([p]);
351   process.nextTick(function() {
352     p.then(assert.fail, function() {});
353   });
354 });
355
356 asyncTest('Failing to catch the Promise.all() of a collection that includes a rejected promise triggers unhandledRejection for the returned promise, not the passed promise', function(done) {
357   var e = new Error();
358   onUnhandledSucceed(done, function(reason, promise) {
359     assert.strictEqual(e, reason);
360     assert.strictEqual(p, promise);
361   });
362   var p = Promise.all([Promise.reject(e)]);
363 });
364
365 asyncTest('Waiting setTimeout(, 10) to catch a promise causes an unhandledRejection + rejectionHandled pair', function(done) {
366   clean();
367   var unhandledPromises = [];
368   var e = new Error();
369   process.on('unhandledRejection', function(reason, promise) {
370     assert.strictEqual(e, reason);
371     unhandledPromises.push(promise);
372   });
373   process.on('rejectionHandled', function(promise) {
374     assert.strictEqual(1, unhandledPromises.length);
375     assert.strictEqual(unhandledPromises[0], promise);
376     assert.strictEqual(thePromise, promise);
377     done();
378   });
379
380   var thePromise = new Promise(function() {
381     throw e;
382   });
383   setTimeout(function() {
384     thePromise.then(assert.fail, function(reason) {
385       assert.strictEqual(e, reason);
386     });
387   }, 10);
388 });
389
390 asyncTest('Waiting for some combination of process.nextTick + promise microtasks to attach a catch handler is still soon enough to prevent unhandledRejection', function(done) {
391   var e = new Error();
392   onUnhandledFail(done);
393
394
395   var a = Promise.reject(e);
396   process.nextTick(function() {
397     Promise.resolve().then(function() {
398       process.nextTick(function() {
399         Promise.resolve().then(function() {
400           a.catch(function() {});
401         });
402       });
403     });
404   });
405 });
406
407 asyncTest('Waiting for some combination of process.nextTick + promise microtasks to attach a catch handler is still soon enough to prevent unhandledRejection: inside setImmediate', function(done) {
408   var e = new Error();
409   onUnhandledFail(done);
410
411   setImmediate(function() {
412     var a = Promise.reject(e);
413     process.nextTick(function() {
414       Promise.resolve().then(function() {
415         process.nextTick(function() {
416           Promise.resolve().then(function() {
417             a.catch(function() {});
418           });
419         });
420       });
421     });
422   });
423 });
424
425 asyncTest('Waiting for some combination of process.nextTick + promise microtasks to attach a catch handler is still soon enough to prevent unhandledRejection: inside setTimeout', function(done) {
426   var e = new Error();
427   onUnhandledFail(done);
428
429   setTimeout(function() {
430     var a = Promise.reject(e);
431     process.nextTick(function() {
432       Promise.resolve().then(function() {
433         process.nextTick(function() {
434           Promise.resolve().then(function() {
435             a.catch(function() {});
436           });
437         });
438       });
439     });
440   }, 0);
441 });
442
443 asyncTest('Waiting for some combination of promise microtasks + process.nextTick to attach a catch handler is still soon enough to prevent unhandledRejection', function(done) {
444   var e = new Error();
445   onUnhandledFail(done);
446
447
448   var a = Promise.reject(e);
449   Promise.resolve().then(function() {
450     process.nextTick(function() {
451       Promise.resolve().then(function() {
452         process.nextTick(function() {
453           a.catch(function() {});
454         });
455       });
456     });
457   });
458 });
459
460 asyncTest('Waiting for some combination of promise microtasks + process.nextTick to attach a catch handler is still soon enough to prevent unhandledRejection: inside setImmediate', function(done) {
461   var e = new Error();
462   onUnhandledFail(done);
463
464   setImmediate(function() {
465     var a = Promise.reject(e);
466     Promise.resolve().then(function() {
467       process.nextTick(function() {
468         Promise.resolve().then(function() {
469           process.nextTick(function() {
470             a.catch(function() {});
471           });
472         });
473       });
474     });
475   });
476 });
477
478 asyncTest('Waiting for some combination of promise microtasks + process.nextTick to attach a catch handler is still soon enough to prevent unhandledRejection: inside setTimeout', function(done) {
479   var e = new Error();
480   onUnhandledFail(done);
481
482   setTimeout(function() {
483     var a = Promise.reject(e);
484     Promise.resolve().then(function() {
485       process.nextTick(function() {
486         Promise.resolve().then(function() {
487           process.nextTick(function() {
488             a.catch(function() {});
489           });
490         });
491       });
492     });
493   }, 0);
494 });
495
496 asyncTest('setImmediate + promise microtasks is too late to attach a catch handler; unhandledRejection will be triggered in that case. (setImmediate before promise creation/rejection)', function(done) {
497   var e = new Error();
498   onUnhandledSucceed(done, function(reason, promise) {
499     assert.strictEqual(e, reason);
500     assert.strictEqual(p, promise);
501   });
502   var p = Promise.reject(e);
503   setImmediate(function() {
504     Promise.resolve().then(function () {
505       p.catch(function(){});
506     });
507   });
508 });
509
510 asyncTest('setImmediate + promise microtasks is too late to attach a catch handler; unhandledRejection will be triggered in that case (setImmediate before promise creation/rejection)', function(done) {
511   onUnhandledSucceed(done, function(reason, promise) {
512     assert.strictEqual(undefined, reason);
513     assert.strictEqual(p, promise);
514   });
515   setImmediate(function() {
516     Promise.resolve().then(function () {
517       Promise.resolve().then(function () {
518         Promise.resolve().then(function () {
519           Promise.resolve().then(function () {
520             p.catch(function(){});
521           });
522         });
523       });
524     });
525   });
526   var p = Promise.reject();
527 });
528
529 asyncTest('setImmediate + promise microtasks is too late to attach a catch handler; unhandledRejection will be triggered in that case (setImmediate after promise creation/rejection)', function(done) {
530   onUnhandledSucceed(done, function(reason, promise) {
531     assert.strictEqual(undefined, reason);
532     assert.strictEqual(p, promise);
533   });
534   var p = Promise.reject();
535   setImmediate(function() {
536     Promise.resolve().then(function () {
537       Promise.resolve().then(function () {
538         Promise.resolve().then(function () {
539           Promise.resolve().then(function () {
540             p.catch(function(){});
541           });
542         });
543       });
544     });
545   });
546 });
547
548 asyncTest('Promise unhandledRejection handler does not interfere with domain error handlers being given exceptions thrown from nextTick.', function(done) {
549   var d = domain.create();
550   var domainReceivedError;
551   d.on('error', function(e) {
552     domainReceivedError = e;
553   });
554   d.run(function() {
555     var e = new Error('error');
556     var domainError = new Error('domain error');
557     onUnhandledSucceed(done, function(reason, promise) {
558       assert.strictEqual(reason, e);
559       assert.strictEqual(domainReceivedError, domainError);
560       d.dispose();
561     });
562     var a = Promise.reject(e);
563     process.nextTick(function() {
564       throw domainError;
565     });
566   });
567 });
568
569 asyncTest('nextTick is immediately scheduled when called inside an event handler', function(done) {
570   clean();
571   var e = new Error('error');
572   process.on('unhandledRejection', function(reason, promise) {
573     var order = [];
574     process.nextTick(function() {
575       order.push(1);
576     });
577     setTimeout(function() {
578       order.push(2);
579       assert.deepEqual([1,2], order);
580       done();
581     }, 1);
582   });
583   Promise.reject(e);
584 });
585
586 asyncTest('Throwing an error inside a rejectionHandled handler goes to unhandledException, and does not cause .catch() to throw an exception', function(done) {
587   clean();
588   var e = new Error();
589   var e2 = new Error();
590   var tearDownException = setupException(function(err) {
591       assert.equal(e2, err);
592       tearDownException();
593       done();
594   });
595   process.on('rejectionHandled', function() {
596     throw e2;
597   });
598   var p = Promise.reject(e);
599   setTimeout(function() {
600     try {
601       p.catch(function(){});
602     } catch (e) {
603       done(new Error('fail'));
604     }
605   }, 1);
606 });