Skip to content

Commit bea1ee8

Browse files
committed
test: make crashOnUnhandleRejection opt-out
This commit removes `common.crashOnUnhandledRejection()` and adds `common.disableCrashOnUnhandledRejection()`. To reduce the risk of mistakes and make writing tests that involve promises simpler, always install the unhandledRejection hook in tests and provide a way to disable it for the rare cases where it's needed. PR-URL: #21849 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]>
1 parent 9817e40 commit bea1ee8

File tree

117 files changed

+58
-241
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+58
-241
lines changed

doc/guides/writing-tests.md

+9-19
Original file line numberDiff line numberDiff line change
@@ -225,34 +225,24 @@ countdown.dec(); // The countdown callback will be invoked now.
225225

226226
#### Testing promises
227227

228-
When writing tests involving promises, either make sure that the
229-
`onFulfilled` or the `onRejected` handler is wrapped in
230-
`common.mustCall()` or `common.mustNotCall()` accordingly, or
231-
call `common.crashOnUnhandledRejection()` in the top level of the
232-
test to make sure that unhandled rejections would result in a test
233-
failure. For example:
228+
When writing tests involving promises, it is generally good to wrap the
229+
`onFulfilled` handler, otherwise the test could successfully finish if the
230+
promise never resolves (pending promises do not keep the event loop alive). The
231+
`common` module automatically adds a handler that makes the process crash - and
232+
hence, the test fail - in the case of an `unhandledRejection` event. It is
233+
possible to disable it with `common.disableCrashOnUnhandledRejection()` if
234+
needed.
234235

235236
```javascript
236237
const common = require('../common');
237238
const assert = require('assert');
238239
const fs = require('fs').promises;
239240

240-
// Use `common.crashOnUnhandledRejection()` to make sure unhandled rejections
241-
// will fail the test.
242-
common.crashOnUnhandledRejection();
243-
244-
// Or, wrap the `onRejected` handler in `common.mustNotCall()`.
245-
fs.writeFile('test-file', 'test').catch(common.mustNotCall());
246-
247-
// Or, wrap the `onFulfilled` handler in `common.mustCall()`.
248-
// If there are assertions in the `onFulfilled` handler, wrap
249-
// the next `onRejected` handler in `common.mustNotCall()`
250-
// to handle potential failures.
241+
// Wrap the `onFulfilled` handler in `common.mustCall()`.
251242
fs.readFile('test-file').then(
252243
common.mustCall(
253244
(content) => assert.strictEqual(content.toString(), 'test2')
254-
))
255-
.catch(common.mustNotCall());
245+
));
256246
```
257247

258248
### Flags

test/addons-napi/test_promise/test.js

-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ const common = require('../../common');
77
const assert = require('assert');
88
const test_promise = require(`./build/${common.buildType}/test_promise`);
99

10-
common.crashOnUnhandledRejection();
11-
1210
// A resolution
1311
{
1412
const expected_result = 42;

test/addons-napi/test_threadsafe_function/test.js

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ const expectedArray = (function(arrayLength) {
1212
return result;
1313
})(binding.ARRAY_LENGTH);
1414

15-
common.crashOnUnhandledRejection();
16-
1715
// Handle the rapid teardown test case as the child process. We unref the
1816
// thread-safe function after we have received two values. This causes the
1917
// process to exit and the environment cleanup handler to be invoked.

test/addons/callback-scope/test-resolve-async.js

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const common = require('../../common');
44
const assert = require('assert');
55
const { testResolveAsync } = require(`./build/${common.buildType}/binding`);
66

7-
common.crashOnUnhandledRejection();
8-
97
let called = false;
108
testResolveAsync().then(() => { called = true; });
119

test/addons/make-callback-recurse/test.js

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ const makeCallback = binding.makeCallback;
99
// Make sure this is run in the future.
1010
const mustCallCheckDomains = common.mustCall(checkDomains);
1111

12-
common.crashOnUnhandledRejection();
13-
1412
// Make sure that using MakeCallback allows the error to propagate.
1513
assert.throws(function() {
1614
makeCallback({}, function() {

test/async-hooks/test-promise.chain-promise-before-init-hooks.js

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ const { checkInvocations } = require('./hook-checks');
88
if (!common.isMainThread)
99
common.skip('Worker bootstrapping works differently -> different async IDs');
1010

11-
common.crashOnUnhandledRejection();
12-
1311
const p = new Promise(common.mustCall(function executor(resolve, reject) {
1412
resolve(5);
1513
}));

test/async-hooks/test-promise.js

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ const { checkInvocations } = require('./hook-checks');
99
if (!common.isMainThread)
1010
common.skip('Worker bootstrapping works differently -> different async IDs');
1111

12-
common.crashOnUnhandledRejection();
13-
1412
const hooks = initHooks();
1513

1614
hooks.enable();

test/common/README.md

+8-7
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,19 @@ symlinks
5555
([SeCreateSymbolicLinkPrivilege](https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx)).
5656
On non-Windows platforms, this always returns `true`.
5757

58-
### crashOnUnhandledRejection()
59-
60-
Installs a `process.on('unhandledRejection')` handler that crashes the process
61-
after a tick. This is useful for tests that use Promises and need to make sure
62-
no unexpected rejections occur, because currently they result in silent
63-
failures.
64-
6558
### ddCommand(filename, kilobytes)
6659
* return [&lt;Object>]
6760

6861
Platform normalizes the `dd` command
6962

63+
### disableCrashOnUnhandledRejection()
64+
65+
Removes the `process.on('unhandledRejection')` handler that crashes the process
66+
after a tick. The handler is useful for tests that use Promises and need to make
67+
sure no unexpected rejections occur, because currently they result in silent
68+
failures. However, it is useful in some rare cases to disable it, for example if
69+
the `unhandledRejection` hook is directly used by the test.
70+
7071
### enoughTestMem
7172
* [&lt;boolean>]
7273

test/common/index.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -815,9 +815,10 @@ exports.getBufferSources = function getBufferSources(buf) {
815815
};
816816

817817
// Crash the process on unhandled rejections.
818-
exports.crashOnUnhandledRejection = function() {
819-
process.on('unhandledRejection',
820-
(err) => process.nextTick(() => { throw err; }));
818+
const crashOnUnhandledRejection = (err) => { throw err; };
819+
process.on('unhandledRejection', crashOnUnhandledRejection);
820+
exports.disableCrashOnUnhandledRejection = function() {
821+
process.removeListener('unhandledRejection', crashOnUnhandledRejection);
821822
};
822823

823824
exports.getTTYfd = function getTTYfd() {

test/common/index.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const {
5252
skipIf32Bits,
5353
getArrayBufferViews,
5454
getBufferSources,
55-
crashOnUnhandledRejection,
55+
disableCrashOnUnhandledRejection,
5656
getTTYfd,
5757
runWithInvalidFD,
5858
hijackStdout,
@@ -112,7 +112,7 @@ export {
112112
skipIf32Bits,
113113
getArrayBufferViews,
114114
getBufferSources,
115-
crashOnUnhandledRejection,
115+
disableCrashOnUnhandledRejection,
116116
getTTYfd,
117117
runWithInvalidFD,
118118
hijackStdout,

test/common/inspector-helper.js

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ function spawnChildProcess(inspectorFlags, scriptContents, scriptFile) {
2525
const handler = tearDown.bind(null, child);
2626
process.on('exit', handler);
2727
process.on('uncaughtException', handler);
28+
common.disableCrashOnUnhandledRejection();
2829
process.on('unhandledRejection', handler);
2930
process.on('SIGINT', handler);
3031

test/es-module/test-esm-dynamic-import.js

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ const assert = require('assert');
55
const { URL } = require('url');
66
const vm = require('vm');
77

8-
common.crashOnUnhandledRejection();
9-
108
const relativePath = '../fixtures/es-modules/test-esm-ok.mjs';
119
const absolutePath = require.resolve('../fixtures/es-modules/test-esm-ok.mjs');
1210
const targetURL = new URL('file:///');

test/es-module/test-esm-error-cache.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
// Flags: --experimental-modules
44

5-
const common = require('../common');
5+
require('../common');
66
const assert = require('assert');
77

8-
common.crashOnUnhandledRejection();
9-
108
const file = '../fixtures/syntax/bad_syntax.js';
119

1210
let error;

test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs
22

3-
import {
4-
crashOnUnhandledRejection,
5-
expectsError
6-
} from '../common';
7-
8-
crashOnUnhandledRejection();
3+
import { expectsError } from '../common';
94

105
import('test').catch(expectsError({
116
code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',

test/es-module/test-esm-throw-undefined.mjs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Flags: --experimental-modules
2-
/* eslint-disable node-core/required-modules */
3-
import common from '../common/index.js';
2+
import '../common';
43
import assert from 'assert';
54

65
async function doTest() {
@@ -12,5 +11,4 @@ async function doTest() {
1211
);
1312
}
1413

15-
common.crashOnUnhandledRejection();
1614
doTest();

test/internet/test-dns-any.js

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ const net = require('net');
99
let running = false;
1010
const queue = [];
1111

12-
common.crashOnUnhandledRejection();
13-
1412
const dnsPromises = dns.promises;
1513
const isIPv4 = net.isIPv4;
1614
const isIPv6 = net.isIPv6;

test/internet/test-dns-ipv4.js

-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ const net = require('net');
77
const util = require('util');
88
const isIPv4 = net.isIPv4;
99

10-
common.crashOnUnhandledRejection();
11-
1210
const dnsPromises = dns.promises;
1311
let running = false;
1412
const queue = [];

test/internet/test-dns-ipv6.js

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const { addresses } = require('../common/internet');
44
if (!common.hasIPv6)
55
common.skip('this test, no IPv6 support');
66

7-
common.crashOnUnhandledRejection();
8-
97
const assert = require('assert');
108
const dns = require('dns');
119
const net = require('net');

test/internet/test-dns-promises-resolve.js

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const assert = require('assert');
44

55
const dnsPromises = require('dns').promises;
66

7-
common.crashOnUnhandledRejection();
8-
97
// Error when rrtype is invalid.
108
{
119
const rrtype = 'DUMMY';

test/internet/test-dns-txt-sigsegv.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
'use strict';
2-
const common = require('../common');
2+
require('../common');
33
const assert = require('assert');
44
const dns = require('dns');
55
const dnsPromises = dns.promises;
66

7-
common.crashOnUnhandledRejection();
8-
97
(async function() {
108
const result = await dnsPromises.resolveTxt('www.microsoft.com');
119
assert.strictEqual(result.length, 0);

test/internet/test-dns.js

-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ const isIPv6 = net.isIPv6;
3030
const util = require('util');
3131
const dnsPromises = dns.promises;
3232

33-
common.crashOnUnhandledRejection();
34-
3533
let expected = 0;
3634
let completed = 0;
3735
let running = false;

test/known_issues/test-inspector-cluster-port-clash.js

-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ if (process.config.variables.v8_enable_inspector === 0) {
2323
const cluster = require('cluster');
2424
const net = require('net');
2525

26-
common.crashOnUnhandledRejection();
27-
2826
const ports = [process.debugPort];
2927
const clashPort = process.debugPort + 2;
3028
function serialFork() {
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Flags: --trace-warnings
22
'use strict';
3-
require('../common');
3+
const common = require('../common');
4+
common.disableCrashOnUnhandledRejection();
45
const p = Promise.reject(new Error('This was rejected'));
56
setImmediate(() => p.catch(() => {}));

test/parallel/test-assert-async.js

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ const assert = require('assert');
55
// Test assert.rejects() and assert.doesNotReject() by checking their
66
// expected output and by verifying that they do not work sync
77

8-
common.crashOnUnhandledRejection();
9-
108
// Run all tests in parallel and check their outcome at the end.
119
const promises = [];
1210

test/parallel/test-async-hooks-disable-during-promise.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22
const common = require('../common');
33
const async_hooks = require('async_hooks');
4-
common.crashOnUnhandledRejection();
54

65
if (!common.isMainThread)
76
common.skip('Worker bootstrapping works differently -> different AsyncWraps');

test/parallel/test-async-hooks-enable-during-promise.js

-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
const common = require('../common');
33
const async_hooks = require('async_hooks');
44

5-
common.crashOnUnhandledRejection();
6-
75
Promise.resolve(1).then(common.mustCall(() => {
86
async_hooks.createHook({
97
init: common.mustCall(),

test/parallel/test-async-hooks-promise-enable-disable.js

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ let p_resource = null;
88
let p_er = null;
99
let p_inits = 0;
1010

11-
common.crashOnUnhandledRejection();
12-
1311
// Not useful to place common.mustCall() around 'exit' event b/c it won't be
1412
// able to check it anyway.
1513
process.on('exit', (code) => {

test/parallel/test-async-hooks-promise-triggerid.js

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ const async_hooks = require('async_hooks');
66
if (!common.isMainThread)
77
common.skip('Worker bootstrapping works differently -> different async IDs');
88

9-
common.crashOnUnhandledRejection();
10-
119
const promiseAsyncIds = [];
1210

1311
async_hooks.createHook({

test/parallel/test-async-wrap-pop-id-during-load.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use strict';
22

3-
require('../common');
3+
const common = require('../common');
44

55
if (process.argv[2] === 'async') {
6+
common.disableCrashOnUnhandledRejection();
67
async function fn() {
78
fn();
89
throw new Error();

test/parallel/test-async-wrap-promise-after-enabled.js

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ const async_hooks = require('async_hooks');
1212

1313
const seenEvents = [];
1414

15-
common.crashOnUnhandledRejection();
16-
1715
const p = new Promise((resolve) => resolve(1));
1816
p.then(() => seenEvents.push('then'));
1917

test/parallel/test-c-ares.js

-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
const common = require('../common');
2424
const assert = require('assert');
2525

26-
common.crashOnUnhandledRejection();
27-
2826
const dns = require('dns');
2927
const dnsPromises = dns.promises;
3028

test/parallel/test-child-process-promisified.js

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const assert = require('assert');
44
const child_process = require('child_process');
55
const { promisify } = require('util');
66

7-
common.crashOnUnhandledRejection();
8-
97
const exec = promisify(child_process.exec);
108
const execFile = promisify(child_process.execFile);
119

test/parallel/test-dns-lookup.js

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ const cares = process.binding('cares_wrap');
55
const dns = require('dns');
66
const dnsPromises = dns.promises;
77

8-
common.crashOnUnhandledRejection();
9-
108
// Stub `getaddrinfo` to *always* error.
119
cares.getaddrinfo = () => process.binding('uv').UV_ENOENT;
1210

test/parallel/test-dns-resolveany-bad-ancount.js

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ const assert = require('assert');
66
const dgram = require('dgram');
77
const dnsPromises = dns.promises;
88

9-
common.crashOnUnhandledRejection();
10-
119
const server = dgram.createSocket('udp4');
1210

1311
server.on('message', common.mustCall((msg, { address, port }) => {

test/parallel/test-dns-resolveany.js

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ const assert = require('assert');
66
const dgram = require('dgram');
77
const dnsPromises = dns.promises;
88

9-
common.crashOnUnhandledRejection();
10-
119
const answers = [
1210
{ type: 'A', address: '1.2.3.4', ttl: 123 },
1311
{ type: 'AAAA', address: '::42', ttl: 123 },

0 commit comments

Comments
 (0)