Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 9 additions & 19 deletions doc/guides/writing-tests.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -225,34 +225,24 @@ countdown.dec(); // The countdown callback will be invoked now.

#### Testing promises

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

```javascript
const common = require('../common');
const assert = require('assert');
const fs = require('fs').promises;

// Use `common.crashOnUnhandledRejection()` to make sure unhandled rejections
// will fail the test.
common.crashOnUnhandledRejection();

// Or, wrap the `onRejected` handler in `common.mustNotCall()`.
fs.writeFile('test-file', 'test').catch(common.mustNotCall());

// Or, wrap the `onFulfilled` handler in `common.mustCall()`.
// If there are assertions in the `onFulfilled` handler, wrap
// the next `onRejected` handler in `common.mustNotCall()`
// to handle potential failures.
// Wrap the `onFulfilled` handler in `common.mustCall()`.
fs.readFile('test-file').then(
common.mustCall(
(content) => assert.strictEqual(content.toString(), 'test2')
))
.catch(common.mustNotCall());
));
```

### Flags
Expand Down
2 changes: 0 additions & 2 deletions test/addons-napi/test_promise/test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,8 +7,6 @@ const common = require('../../common');
constassert=require('assert');
consttest_promise=require(`./build/${common.buildType}/test_promise`);

common.crashOnUnhandledRejection();

// A resolution
{
constexpected_result=42;
Expand Down
2 changes: 0 additions & 2 deletions test/addons-napi/test_threadsafe_function/test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,8 +12,6 @@ const expectedArray = (function(arrayLength) {
returnresult;
})(binding.ARRAY_LENGTH);

common.crashOnUnhandledRejection();

// Handle the rapid teardown test case as the child process. We unref the
// thread-safe function after we have received two values. This causes the
// process to exit and the environment cleanup handler to be invoked.
Expand Down
2 changes: 0 additions & 2 deletions test/addons/callback-scope/test-resolve-async.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,8 +4,6 @@ const common = require('../../common');
const assert = require('assert');
const { testResolveAsync } = require(`./build/${common.buildType}/binding`);

common.crashOnUnhandledRejection();

let called = false;
testResolveAsync().then(() => { called = true; });

Expand Down
2 changes: 0 additions & 2 deletions test/addons/make-callback-recurse/test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,8 +9,6 @@ const makeCallback = binding.makeCallback;
// Make sure this is run in the future.
const mustCallCheckDomains = common.mustCall(checkDomains);

common.crashOnUnhandledRejection();

// Make sure that using MakeCallback allows the error to propagate.
assert.throws(function() {
makeCallback({}, function() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,8 +8,6 @@ const { checkInvocations } = require('./hook-checks');
if (!common.isMainThread)
common.skip('Worker bootstrapping works differently -> different async IDs');

common.crashOnUnhandledRejection();

const p = new Promise(common.mustCall(function executor(resolve, reject) {
resolve(5);
}));
Expand Down
2 changes: 0 additions & 2 deletions test/async-hooks/test-promise.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,8 +9,6 @@ const { checkInvocations } = require('./hook-checks');
if (!common.isMainThread)
common.skip('Worker bootstrapping works differently -> different async IDs');

common.crashOnUnhandledRejection();

const hooks = initHooks();

hooks.enable();
Expand Down
15 changes: 8 additions & 7 deletions test/common/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,18 +55,19 @@ symlinks
([SeCreateSymbolicLinkPrivilege](https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx)).
On non-Windows platforms, this always returns `true`.

### crashOnUnhandledRejection()

Installs a `process.on('unhandledRejection')` handler that crashes the process
after a tick. This is useful for tests that use Promises and need to make sure
no unexpected rejections occur, because currently they result in silent
failures.

### ddCommand(filename, kilobytes)
* return [<Object>]

Platform normalizes the `dd` command

### disableCrashOnUnhandledRejection()

Removes the `process.on('unhandledRejection')` handler that crashes the process
after a tick. The handler is useful for tests that use Promises and need to make
sure no unexpected rejections occur, because currently they result in silent
failures. However, it is useful in some rare cases to disable it, for example if
the `unhandledRejection` hook is directly used by the test.

### enoughTestMem
* [<boolean>]

Expand Down
7 changes: 4 additions & 3 deletions test/common/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -814,9 +814,10 @@ exports.getBufferSources = function getBufferSources(buf) {
};

// Crash the process on unhandled rejections.
exports.crashOnUnhandledRejection = function() {
process.on('unhandledRejection',
(err) => process.nextTick(() => { throw err; }));
const crashOnUnhandledRejection = (err) => { throw err; };
process.on('unhandledRejection', crashOnUnhandledRejection);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was it intentional to drop the process.nextTick()?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was not. Do you think we should put it back?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure -- I was confused because the README says:

Removes the process.on('unhandledRejection') handler that crashes the process
after a tick.

so I assumed the process.nextTick() in the original code was intentional. ¯\_(ツ)_/¯

@targostargosJul 24, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/cc @addaleax since you implemented it initially. Was there a reason to throw in the next tick? I can't see any difference on a local test with or without process.nextTick.

Edit: commit

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@targos I don’t remember – I’d assume dropping it is okay.

exports.disableCrashOnUnhandledRejection = function() {
process.removeListener('unhandledRejection', crashOnUnhandledRejection);
};

exports.getTTYfd = function getTTYfd() {
Expand Down
4 changes: 2 additions & 2 deletions test/common/index.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,7 +52,7 @@ const {
skipIf32Bits,
getArrayBufferViews,
getBufferSources,
crashOnUnhandledRejection,
disableCrashOnUnhandledRejection,
getTTYfd,
runWithInvalidFD,
hijackStdout,
Expand DownExpand Up@@ -112,7 +112,7 @@ export {
skipIf32Bits,
getArrayBufferViews,
getBufferSources,
crashOnUnhandledRejection,
disableCrashOnUnhandledRejection,
getTTYfd,
runWithInvalidFD,
hijackStdout,
Expand Down
1 change: 1 addition & 0 deletions test/common/inspector-helper.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ function spawnChildProcess(inspectorFlags, scriptContents, scriptFile) {
const handler = tearDown.bind(null, child);
process.on('exit', handler);
process.on('uncaughtException', handler);
common.disableCrashOnUnhandledRejection();
process.on('unhandledRejection', handler);
process.on('SIGINT', handler);

Expand Down
2 changes: 0 additions & 2 deletions test/es-module/test-esm-dynamic-import.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,8 +5,6 @@ const assert = require('assert');
const { URL } = require('url');
const vm = require('vm');

common.crashOnUnhandledRejection();

const relativePath = '../fixtures/es-modules/test-esm-ok.mjs';
const absolutePath = require.resolve('../fixtures/es-modules/test-esm-ok.mjs');
const targetURL = new URL('file:///');
Expand Down
4 changes: 1 addition & 3 deletions test/es-module/test-esm-error-cache.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,11 +2,9 @@

// Flags: --experimental-modules

constcommon=require('../common');
require('../common');
constassert=require('assert');

common.crashOnUnhandledRejection();

constfile='../fixtures/syntax/bad_syntax.js';

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

import{
crashOnUnhandledRejection,
expectsError
}from'../common';

crashOnUnhandledRejection();
import{expectsError}from'../common';

import('test').catch(expectsError({
code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',
Expand Down
4 changes: 1 addition & 3 deletions test/es-module/test-esm-throw-undefined.mjs
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
// Flags: --experimental-modules
/* eslint-disable node-core/required-modules */
importcommonfrom'../common/index.js';
import'../common';
importassertfrom'assert';

asyncfunctiondoTest(){
Expand All@@ -12,5 +11,4 @@ async function doTest() {
);
}

common.crashOnUnhandledRejection();
doTest();
2 changes: 0 additions & 2 deletions test/internet/test-dns-any.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,8 +9,6 @@ const net = require('net');
let running = false;
const queue = [];

common.crashOnUnhandledRejection();

const dnsPromises = dns.promises;
const isIPv4 = net.isIPv4;
const isIPv6 = net.isIPv6;
Expand Down
2 changes: 0 additions & 2 deletions test/internet/test-dns-ipv4.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,8 +7,6 @@ const net = require('net');
constutil=require('util');
constisIPv4=net.isIPv4;

common.crashOnUnhandledRejection();

constdnsPromises=dns.promises;
letrunning=false;
constqueue=[];
Expand Down
2 changes: 0 additions & 2 deletions test/internet/test-dns-ipv6.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,8 +4,6 @@ const { addresses } = require('../common/internet');
if(!common.hasIPv6)
common.skip('this test, no IPv6 support');

common.crashOnUnhandledRejection();

constassert=require('assert');
constdns=require('dns');
constnet=require('net');
Expand Down
4 changes: 1 addition & 3 deletions test/internet/test-dns-txt-sigsegv.js
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
'use strict';
const common = require('../common');
require('../common');
const assert = require('assert');
const dns = require('dns');
const dnsPromises = dns.promises;

common.crashOnUnhandledRejection();

(async function() {
const result = await dnsPromises.resolveTxt('www.microsoft.com');
assert.strictEqual(result.length, 0);
Expand Down
2 changes: 0 additions & 2 deletions test/internet/test-dns.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,6 @@ const isIPv6 = net.isIPv6;
constutil=require('util');
constdnsPromises=dns.promises;

common.crashOnUnhandledRejection();

letexpected=0;
letcompleted=0;
letrunning=false;
Expand Down
2 changes: 0 additions & 2 deletions test/known_issues/test-inspector-cluster-port-clash.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,8 +23,6 @@ if (process.config.variables.v8_enable_inspector === 0) {
const cluster = require('cluster');
const net = require('net');

common.crashOnUnhandledRejection();

const ports = [process.debugPort];
const clashPort = process.debugPort + 2;
function serialFork() {
Expand Down
3 changes: 2 additions & 1 deletion test/message/unhandled_promise_trace_warnings.js
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
// Flags: --trace-warnings
'use strict';
require('../common');
const common = require('../common');
common.disableCrashOnUnhandledRejection();
const p = Promise.reject(new Error('This was rejected'));
setImmediate(() => p.catch(() => {}));
2 changes: 0 additions & 2 deletions test/parallel/test-assert-async.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,8 +5,6 @@ const assert = require('assert');
// Test assert.rejects() and assert.doesNotReject() by checking their
// expected output and by verifying that they do not work sync

common.crashOnUnhandledRejection();

// Run all tests in parallel and check their outcome at the end.
constpromises=[];

Expand Down
1 change: 0 additions & 1 deletion test/parallel/test-async-hooks-disable-during-promise.js
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
'use strict';
constcommon=require('../common');
constasync_hooks=require('async_hooks');
common.crashOnUnhandledRejection();

if(!common.isMainThread)
common.skip('Worker bootstrapping works differently -> different AsyncWraps');
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-async-hooks-enable-during-promise.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,8 +2,6 @@
const common = require('../common');
const async_hooks = require('async_hooks');

common.crashOnUnhandledRejection();

Promise.resolve(1).then(common.mustCall(() => {
async_hooks.createHook({
init: common.mustCall(),
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-async-hooks-promise-enable-disable.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,8 +8,6 @@ let p_resource = null;
letp_er=null;
letp_inits=0;

common.crashOnUnhandledRejection();

// Not useful to place common.mustCall() around 'exit' event b/c it won't be
// able to check it anyway.
process.on('exit',(code)=>{
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-async-hooks-promise-triggerid.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,8 +6,6 @@ const async_hooks = require('async_hooks');
if(!common.isMainThread)
common.skip('Worker bootstrapping works differently -> different async IDs');

common.crashOnUnhandledRejection();

constpromiseAsyncIds=[];

async_hooks.createHook({
Expand Down
3 changes: 2 additions & 1 deletion test/parallel/test-async-wrap-pop-id-during-load.js
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
'use strict';

require('../common');
constcommon=require('../common');

if(process.argv[2]==='async'){
common.disableCrashOnUnhandledRejection();
asyncfunctionfn(){
fn();
thrownewError();
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-async-wrap-promise-after-enabled.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,8 +12,6 @@ const async_hooks = require('async_hooks');

const seenEvents = [];

common.crashOnUnhandledRejection();

const p = new Promise((resolve) => resolve(1));
p.then(() => seenEvents.push('then'));

Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-c-ares.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,8 +23,6 @@
const common = require('../common');
const assert = require('assert');

common.crashOnUnhandledRejection();

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

Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-child-process-promisified.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,8 +4,6 @@ const assert = require('assert');
constchild_process=require('child_process');
const{ promisify }=require('util');

common.crashOnUnhandledRejection();

constexec=promisify(child_process.exec);
constexecFile=promisify(child_process.execFile);

Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-dns-lookup.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,8 +5,6 @@ const cares = process.binding('cares_wrap');
const dns = require('dns');
const dnsPromises = dns.promises;

common.crashOnUnhandledRejection();

// Stub `getaddrinfo` to *always* error.
cares.getaddrinfo = () => process.binding('uv').UV_ENOENT;

Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-dns-promises-resolve.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,8 +4,6 @@ const assert = require('assert');

constdnsPromises=require('dns').promises;

common.crashOnUnhandledRejection();

// Error when rrtype is invalid.
{
constrrtype='DUMMY';
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-dns-resolveany-bad-ancount.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,8 +6,6 @@ const assert = require('assert');
const dgram = require('dgram');
const dnsPromises = dns.promises;

common.crashOnUnhandledRejection();

const server = dgram.createSocket('udp4');

server.on('message', common.mustCall((msg, { address, port }) => {
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-dns-resolveany.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,8 +6,6 @@ const assert = require('assert');
const dgram = require('dgram');
const dnsPromises = dns.promises;

common.crashOnUnhandledRejection();

const answers = [
{ type: 'A', address: '1.2.3.4', ttl: 123 },
{ type: 'AAAA', address: '::42', ttl: 123 },
Expand Down
Loading