Skip to content

Commit 60ef7a0

Browse files
committedMay 29, 2019
Fixes #1468, stabilize unhandled rejection tests
1 parent 8991667 commit 60ef7a0

File tree

7 files changed

+157
-35
lines changed

7 files changed

+157
-35
lines changed
 

‎src/debuggability.js

+119-10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,77 @@ var longStackTraces = !!(util.env("BLUEBIRD_LONG_STACK_TRACES") != 0 &&
3131
var wForgottenReturn = util.env("BLUEBIRD_W_FORGOTTEN_RETURN") != 0 &&
3232
(warnings || !!util.env("BLUEBIRD_W_FORGOTTEN_RETURN"));
3333

34+
var deferUnhandledRejectionCheck;
35+
(function() {
36+
var promises = [];
37+
38+
function unhandledRejectionCheck() {
39+
for (var i = 0; i < promises.length; ++i) {
40+
promises[i]._notifyUnhandledRejection();
41+
}
42+
unhandledRejectionClear();
43+
}
44+
45+
function unhandledRejectionClear() {
46+
promises.length = 0;
47+
}
48+
49+
if (util.isNode) {
50+
deferUnhandledRejectionCheck = (function() {
51+
var timers = require("timers");
Has conversations. Original line has conversations.
52+
var timerSetTimeout = timers.setTimeout;
53+
var timer = timerSetTimeout(unhandledRejectionCheck, 1);
54+
timer.unref();
55+
56+
return function(promise) {
57+
promises.push(promise);
58+
if (typeof timer.refresh === "function") {
59+
timer.refresh();
60+
} else {
61+
timerSetTimeout(unhandledRejectionCheck, 1).unref();
62+
}
63+
};
64+
})();
65+
} else if (typeof document === "object" && document.createElement) {
66+
deferUnhandledRejectionCheck = (function() {
67+
var iframeSetTimeout;
68+
69+
function checkIframe() {
70+
if (document.body) {
71+
var iframe = document.createElement("iframe");
72+
document.body.appendChild(iframe);
73+
if (iframe.contentWindow &&
74+
iframe.contentWindow.setTimeout) {
75+
iframeSetTimeout = iframe.contentWindow.setTimeout;
76+
}
77+
document.body.removeChild(iframe);
78+
}
79+
}
80+
checkIframe();
81+
return function(promise) {
82+
promises.push(promise);
83+
if (iframeSetTimeout) {
84+
iframeSetTimeout(unhandledRejectionCheck, 1);
85+
} else {
86+
checkIframe();
87+
}
88+
};
89+
})();
90+
} else {
91+
deferUnhandledRejectionCheck = function(promise) {
92+
promises.push(promise);
93+
setTimeout(unhandledRejectionCheck, 1);
94+
};
95+
}
96+
97+
es5.defineProperty(Promise, "_unhandledRejectionCheck", {
98+
value: unhandledRejectionCheck
99+
});
100+
es5.defineProperty(Promise, "_unhandledRejectionClear", {
101+
value: unhandledRejectionClear
102+
});
103+
})();
104+
34105
Promise.prototype.suppressUnhandledRejections = function() {
35106
var target = this._target();
36107
target._bitField = ((target._bitField & (~IS_REJECTION_UNHANDLED)) |
@@ -40,10 +111,7 @@ Promise.prototype.suppressUnhandledRejections = function() {
40111
Promise.prototype._ensurePossibleRejectionHandled = function () {
41112
if ((this._bitField & IS_REJECTION_IGNORED) !== 0) return;
42113
this._setRejectionIsUnhandled();
43-
var self = this;
44-
setTimeout(function() {
45-
self._notifyUnhandledRejection();
46-
}, 1);
114+
deferUnhandledRejectionCheck(this);
47115
};
48116

49117
Promise.prototype._notifyUnhandledRejectionIsHandled = function () {
@@ -144,46 +212,87 @@ Promise.hasLongStackTraces = function () {
144212
return config.longStackTraces && longStackTracesIsSupported();
145213
};
146214

215+
216+
var legacyHandlers = {
217+
unhandledrejection: {
218+
before: function() {
219+
var ret = util.global.onunhandledrejection;
220+
util.global.onunhandledrejection = null;
221+
return ret;
222+
},
223+
after: function(fn) {
224+
util.global.onunhandledrejection = fn;
225+
}
226+
},
227+
rejectionhandled: {
228+
before: function() {
229+
var ret = util.global.onrejectionhandled;
230+
util.global.onrejectionhandled = null;
231+
return ret;
232+
},
233+
after: function(fn) {
234+
util.global.onrejectionhandled = fn;
235+
}
236+
}
237+
};
238+
147239
var fireDomEvent = (function() {
240+
var dispatch = function(legacy, e) {
241+
if (legacy) {
242+
var fn;
243+
try {
244+
fn = legacy.before();
245+
return !util.global.dispatchEvent(e);
246+
} finally {
247+
legacy.after(fn);
248+
}
249+
} else {
250+
return !util.global.dispatchEvent(e);
251+
}
252+
};
148253
try {
149254
if (typeof CustomEvent === "function") {
150255
var event = new CustomEvent("CustomEvent");
151256
util.global.dispatchEvent(event);
152257
return function(name, event) {
258+
name = name.toLowerCase();
153259
var eventData = {
154260
detail: event,
155261
cancelable: true
156262
};
157-
var domEvent = new CustomEvent(name.toLowerCase(), eventData);
263+
var domEvent = new CustomEvent(name, eventData);
158264
es5.defineProperty(
159265
domEvent, "promise", {value: event.promise});
160266
es5.defineProperty(
161267
domEvent, "reason", {value: event.reason});
162-
return !util.global.dispatchEvent(domEvent);
268+
269+
return dispatch(legacyHandlers[name], domEvent);
163270
};
164271
// In Firefox < 48 CustomEvent is not available in workers but
165272
// Event is.
166273
} else if (typeof Event === "function") {
167274
var event = new Event("CustomEvent");
168275
util.global.dispatchEvent(event);
169276
return function(name, event) {
170-
var domEvent = new Event(name.toLowerCase(), {
277+
name = name.toLowerCase();
278+
var domEvent = new Event(name, {
171279
cancelable: true
172280
});
173281
domEvent.detail = event;
174282
es5.defineProperty(domEvent, "promise", {value: event.promise});
175283
es5.defineProperty(domEvent, "reason", {value: event.reason});
176-
return !util.global.dispatchEvent(domEvent);
284+
return dispatch(legacyHandlers[name], domEvent);
177285
};
178286
} else {
179287
var event = document.createEvent("CustomEvent");
180288
event.initCustomEvent("testingtheevent", false, true, {});
181289
util.global.dispatchEvent(event);
182290
return function(name, event) {
291+
name = name.toLowerCase();
183292
var domEvent = document.createEvent("CustomEvent");
184-
domEvent.initCustomEvent(name.toLowerCase(), false, true,
293+
domEvent.initCustomEvent(name, false, true,
185294
event);
186-
return !util.global.dispatchEvent(domEvent);
295+
return dispatch(legacyHandlers[name], domEvent);
187296
};
188297
}
189298
} catch (e) {}

‎test/browser/mocha.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -4215,7 +4215,7 @@ function Runnable(title, fn) {
42154215
this.fn = fn;
42164216
this.async = fn && fn.length;
42174217
this.sync = ! this.async;
4218-
this._timeout = 2000;
4218+
this._timeout = 1000 * 60 * 5;
42194219
this._slow = 75;
42204220
this._enableTimeouts = true;
42214221
this.timedOut = false;
@@ -4331,6 +4331,7 @@ Runnable.prototype.resetTimeout = function(){
43314331

43324332
if (!this._enableTimeouts) return;
43334333
this.clearTimeout();
4334+
43344335
this.timer = setTimeout(function(){
43354336
if (!self._enableTimeouts) return;
43364337
self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
@@ -5185,7 +5186,7 @@ function Suite(title, parentContext) {
51855186
this._afterEach = [];
51865187
this._afterAll = [];
51875188
this.root = !title;
5188-
this._timeout = 2000;
5189+
this._timeout = 1000 * 60 * 5;
51895190
this._enableTimeouts = true;
51905191
this._slow = 75;
51915192
this._bail = false;

‎test/mocha/bluebird-multiple-instances.js

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ if (isNodeJS) {
5757
setTimeout(function(){
5858
d1.fulfill();
5959
d2.fulfill();
60+
setTimeout(function() {
61+
Promise1._unhandledRejectionCheck();
62+
Promise2._unhandledRejectionCheck();
63+
}, 100);
6064
}, 1);
6165
return Promise.all([spy1.promise, spy2.promise]);
6266
});

‎test/mocha/helpers/util.js

+11-8
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ module.exports = {
9999
var promise = new Promise(function() {
100100
resolve = arguments[0];
101101
reject = arguments[1];
102-
}).timeout(500);
102+
});
103103
var ret = function(fn) {
104104
ret.callback = fn;
105105
return ret.node;
@@ -122,7 +122,7 @@ module.exports = {
122122
var promise = new Promise(function() {
123123
resolve = arguments[0];
124124
reject = arguments[1];
125-
}).timeout(500);
125+
});
126126
domain = require('domain').create();
127127
domain.on("error", function(e) {
128128
try {
@@ -136,18 +136,18 @@ module.exports = {
136136
return promise;
137137
},
138138

139-
//Since there is only a single handler possible at a time, older
140-
//tests that are run just before this file could affect the results
141-
//that's why there is 500ms limit in grunt file between each test
142-
//beacuse the unhandled rejection handler will run within 100ms right now
143139
onUnhandledFail: function(testFunction) {
140+
Promise._unhandledRejectionClear();
144141
return new Promise(function(resolve, reject) {
145142
var err = new Error("Reporting handled rejection as unhandled from: " +
146143
testFunction);
147144
Promise.onPossiblyUnhandledRejection(function() {
148145
reject(err);
149146
});
150-
Promise.delay(25).then(resolve);
147+
Promise.delay(150).then(function() {
148+
Promise._unhandledRejectionCheck();
149+
resolve();
150+
});
151151
}).lastly(function() {
152152
Promise.onPossiblyUnhandledRejection(null);
153153
});
@@ -183,7 +183,10 @@ module.exports = {
183183
resolve(e);
184184
}
185185
});
186-
Promise.delay(200).then(function() {
186+
Promise.delay(50).then(function() {
187+
Promise._unhandledRejectionCheck();
188+
return Promise.delay(1);
189+
}).then(function() {
187190
var message = "Expected onPossiblyUnhandledRejection to be called " +
188191
total + " times but it was only called " + cur + " times";
189192
reject(new Error(message));

‎test/mocha/unhandled_rejections.js

+17-12
Original file line numberDiff line numberDiff line change
@@ -517,9 +517,11 @@ describe("Promise.onUnhandledRejectionHandled", function() {
517517
var a = new Promise(function(){
518518
throw reason;
519519
});
520-
setTimeout(function(){
520+
521+
setTimeout(function() {
522+
Promise._unhandledRejectionCheck();
521523
a.then(assert.fail, function(){});
522-
}, 200);
524+
}, 1);
523525

524526
return Promise.all([spy1.promise, spy2.promise]);
525527
});
@@ -564,9 +566,10 @@ describe("global events", function() {
564566

565567
var promise = new Promise(function() {throw err;});
566568
setTimeout(function() {
569+
Promise._unhandledRejectionCheck();
567570
promise.then(assert.fail, function(){});
568-
}, 150);
569-
}).timeout(500);
571+
}, 1);
572+
});
570573
});
571574

572575
specify("are fired with local events", function() {
@@ -603,9 +606,10 @@ describe("global events", function() {
603606

604607
var promise = new Promise(function() {throw err;});
605608
setTimeout(function() {
609+
Promise._unhandledRejectionCheck();
606610
promise.then(assert.fail, function(){});
607-
}, 150);
608-
}).timeout(500);
611+
}, 1);
612+
});
609613

610614
});
611615
});
@@ -661,27 +665,28 @@ if (windowDomEventSupported) {
661665
assert.strictEqual(e.detail.promise, promise);
662666
assert.strictEqual(e.detail.reason, undefined);
663667
assert.strictEqual(e.promise, promise);
664-
assert.strictEqual(e.reason, err);
668+
assert.strictEqual(e.reason, undefined);
665669
order.push(3);
666670
});
667671
attachEvent("rejectionhandled", function(e) {
668672
assert.strictEqual(e.detail.promise, promise);
669673
assert.strictEqual(e.detail.reason, undefined);
670674
assert.strictEqual(e.promise, promise);
671-
assert.strictEqual(e.reason, err);
675+
assert.strictEqual(e.reason, undefined);
672676
assert.strictEqual(e.defaultPrevented, true);
673677
order.push(4);
674678
resolve();
675679
});
676680

677681
setTimeout(function() {
682+
Promise._unhandledRejectionCheck();
678683
promise.then(assert.fail, function(r) {
679684
order.push(5);
680685
assert.strictEqual(r, err);
681686
assert.deepEqual(order, [1,2,3,4,5]);
682687
});
683-
}, 150);
684-
}).timeout(500);
688+
}, 1);
689+
});
685690

686691
})
687692
});
@@ -722,7 +727,7 @@ if (windowDomEventSupported) {
722727
worker.postMessage("reject");
723728
}).then(function () {
724729
assert.deepEqual(order, [1, 2]);
725-
}).timeout(500);
730+
});
726731
});
727732
});
728733
}
@@ -796,7 +801,7 @@ describe("issues", function () {
796801

797802
specify("GH-1501-2", function testFunction() {
798803
var ret = onUnhandledFail(testFunction);
799-
Promise.reduce([Promise.delay(100), Promise.reject(new Error("reason"))],
804+
Promise.reduce([Promise.delay(1), Promise.reject(new Error("reason"))],
800805
function() {},
801806
{}).caught(function() {});
802807
return ret;

‎tools/build.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ function buildBrowser(sources, dir, tmpDir, depsRequireCode, minify, npmPackage,
212212
entries: entries,
213213
detectGlobals: false,
214214
standalone: "Promise"
215-
}).exclude('async_hooks');
215+
}).exclude('async_hooks').exclude("timers");
216216
return Promise.promisify(b.bundle, b)().then(function(src) {
217217
var alias = "\
218218
;if (typeof window !== 'undefined' && window !== null) { \

‎tools/mocha_runner.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ module.exports = function mochaRun(progress) {
9494
return Promise.each(testGroup, function(test, index, length) {
9595
var mocha = new Mocha({
9696
reporter: "spec",
97-
timeout: 50000, //200 caused non-deterministic test failures
98-
//when a test uses timeouts just barely under 200 ms
97+
timeout: "300s",
98+
enableTimeouts: true,
9999
slow: Infinity,
100100
bail: true
101101
});

1 commit comments

Comments
 (1)

Trott commented on Oct 2, 2019

@Trott

This commit makes the tests unreliable for me locally on macOS using Node.js 12.11.1. It doesn't fail every time but it fails most of the time if I use npm test and less often if I use node tools/test --run=unhandled_rejections.js:

55 passing (222ms)
  1 failing

  1) issues GH-1487-8:
     Error: Reporting handled rejection as unhandled from: function testFunction() {
        var ret = onUnhandledFail(testFunction);
        var arr = [ Promise.reject( new Error('foo') ) ];
        var p = Promise.resolve( arr );
        p.filter( function() {} ).caught( function() {} );
        return ret;
    }
      at /Users/trott/temp/bluebird/test/mocha/helpers/util.js:142:23
  From previous event:
      at onUnhandledFail (/Users/trott/temp/bluebird/test/mocha/helpers/util.js:141:16)
      at context.testFunction (/Users/trott/temp/bluebird/test/mocha/unhandled_rejections.js:869:19)
      at callFn (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:251:21)
      at Test.Runnable.run (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:244:7)
      at Runner.runTest (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:374:10)
      at /Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:452:12
      at next (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:299:14)
      at /Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:309:7
      at next (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:248:23)
      at /Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:271:7
      at done (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:207:5)
      at callFn (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:262:7)
      at Hook.Runnable.run (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:244:7)
      at next (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:259:10)
      at Immediate.<anonymous> (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:276:5)
      at processImmediate (internal/timers.js:439:21)



Error: Reporting handled rejection as unhandled from: function testFunction() {
        var ret = onUnhandledFail(testFunction);
        var arr = [ Promise.reject( new Error('foo') ) ];
        var p = Promise.resolve( arr );
        p.filter( function() {} ).caught( function() {} );
        return ret;
    }
    at /Users/trott/temp/bluebird/test/mocha/helpers/util.js:142:23
    at onUnhandledFail (/Users/trott/temp/bluebird/test/mocha/helpers/util.js:141:16)
    at context.testFunction (/Users/trott/temp/bluebird/test/mocha/unhandled_rejections.js:869:19)
    at callFn (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:251:21)
    at Test.Runnable.run (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:244:7)
    at Runner.runTest (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:374:10)
    at /Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:452:12
    at next (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:299:14)
    at /Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:309:7
    at next (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:248:23)
    at /Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:271:7
    at done (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:207:5)
    at callFn (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:262:7)
    at Hook.Runnable.run (/Users/trott/temp/bluebird/node_modules/mocha/lib/runnable.js:244:7)
    at next (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:259:10)
    at Immediate.<anonymous> (/Users/trott/temp/bluebird/node_modules/mocha/lib/runner.js:276:5)
From previous event:
    at Object.run (/Users/trott/temp/bluebird/tools/job-runner/job-runner.js:141:27)
    at runTestGroup (/Users/trott/temp/bluebird/tools/test.js:123:22)
    at /Users/trott/temp/bluebird/tools/test.js:261:16
    at processImmediate (internal/timers.js:439:21)
From previous event:
    at Object.<anonymous> (/Users/trott/temp/bluebird/tools/test.js:254:27)
    at Module._compile (internal/modules/cjs/loader.js:945:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:962:10)
    at Module.load (internal/modules/cjs/loader.js:798:32)
    at Function.Module._load (internal/modules/cjs/loader.js:711:12)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1014:10)
    at internal/main/run_main_module.js:17:11
Please sign in to comment.