test: add test for async hooks parity for async/await
Add a basic test ensuring parity between before-after and init-promiseResolve hooks when using async/await. Add ability to initHooks and to checkInvocations utilities to transmit promiseResolve hook as well. See: https://github.com/nodejs/node/issues/20516 PR-URL: https://github.com/nodejs/node/pull/20626 Refs: https://github.com/nodejs/node/issues/20516 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
This commit is contained in:
parent
657723e5c0
commit
e993e45dbf
@ -25,7 +25,7 @@ exports.checkInvocations = function checkInvocations(activity, hooks, stage) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Check that actual invocations for all hooks match the expected invocations
|
// Check that actual invocations for all hooks match the expected invocations
|
||||||
[ 'init', 'before', 'after', 'destroy' ].forEach(checkHook);
|
[ 'init', 'before', 'after', 'destroy', 'promiseResolve' ].forEach(checkHook);
|
||||||
|
|
||||||
function checkHook(k) {
|
function checkHook(k) {
|
||||||
const val = hooks[k];
|
const val = hooks[k];
|
||||||
|
@ -25,6 +25,7 @@ class ActivityCollector {
|
|||||||
onbefore,
|
onbefore,
|
||||||
onafter,
|
onafter,
|
||||||
ondestroy,
|
ondestroy,
|
||||||
|
onpromiseResolve,
|
||||||
logid = null,
|
logid = null,
|
||||||
logtype = null
|
logtype = null
|
||||||
} = {}) {
|
} = {}) {
|
||||||
@ -39,13 +40,16 @@ class ActivityCollector {
|
|||||||
this.onbefore = typeof onbefore === 'function' ? onbefore : noop;
|
this.onbefore = typeof onbefore === 'function' ? onbefore : noop;
|
||||||
this.onafter = typeof onafter === 'function' ? onafter : noop;
|
this.onafter = typeof onafter === 'function' ? onafter : noop;
|
||||||
this.ondestroy = typeof ondestroy === 'function' ? ondestroy : noop;
|
this.ondestroy = typeof ondestroy === 'function' ? ondestroy : noop;
|
||||||
|
this.onpromiseResolve = typeof onpromiseResolve === 'function' ?
|
||||||
|
onpromiseResolve : noop;
|
||||||
|
|
||||||
// Create the hook with which we'll collect activity data
|
// Create the hook with which we'll collect activity data
|
||||||
this._asyncHook = async_hooks.createHook({
|
this._asyncHook = async_hooks.createHook({
|
||||||
init: this._init.bind(this),
|
init: this._init.bind(this),
|
||||||
before: this._before.bind(this),
|
before: this._before.bind(this),
|
||||||
after: this._after.bind(this),
|
after: this._after.bind(this),
|
||||||
destroy: this._destroy.bind(this)
|
destroy: this._destroy.bind(this),
|
||||||
|
promiseResolve: this._promiseResolve.bind(this)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +210,13 @@ class ActivityCollector {
|
|||||||
this.ondestroy(uid);
|
this.ondestroy(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_promiseResolve(uid) {
|
||||||
|
const h = this._getActivity(uid, 'promiseResolve');
|
||||||
|
this._stamp(h, 'promiseResolve');
|
||||||
|
this._maybeLog(uid, h && h.type, 'promiseResolve');
|
||||||
|
this.onpromiseResolve(uid);
|
||||||
|
}
|
||||||
|
|
||||||
_maybeLog(uid, type, name) {
|
_maybeLog(uid, type, name) {
|
||||||
if (this._logid &&
|
if (this._logid &&
|
||||||
(type == null || this._logtype == null || this._logtype === type)) {
|
(type == null || this._logtype == null || this._logtype === type)) {
|
||||||
@ -219,6 +230,7 @@ exports = module.exports = function initHooks({
|
|||||||
onbefore,
|
onbefore,
|
||||||
onafter,
|
onafter,
|
||||||
ondestroy,
|
ondestroy,
|
||||||
|
onpromiseResolve,
|
||||||
allowNoInit,
|
allowNoInit,
|
||||||
logid,
|
logid,
|
||||||
logtype
|
logtype
|
||||||
@ -228,6 +240,7 @@ exports = module.exports = function initHooks({
|
|||||||
onbefore,
|
onbefore,
|
||||||
onafter,
|
onafter,
|
||||||
ondestroy,
|
ondestroy,
|
||||||
|
onpromiseResolve,
|
||||||
allowNoInit,
|
allowNoInit,
|
||||||
logid,
|
logid,
|
||||||
logtype
|
logtype
|
||||||
|
91
test/async-hooks/test-async-await.js
Normal file
91
test/async-hooks/test-async-await.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
// This test ensures async hooks are being properly called
|
||||||
|
// when using async-await mechanics. This involves:
|
||||||
|
// 1. Checking that all initialized promises are being resolved
|
||||||
|
// 2. Checking that for each 'before' corresponding hook 'after' hook is called
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const initHooks = require('./init-hooks');
|
||||||
|
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
const sleep = util.promisify(setTimeout);
|
||||||
|
// either 'inited' or 'resolved'
|
||||||
|
const promisesInitState = new Map();
|
||||||
|
// either 'before' or 'after' AND asyncId must be present in the other map
|
||||||
|
const promisesExecutionState = new Map();
|
||||||
|
|
||||||
|
const hooks = initHooks({
|
||||||
|
oninit,
|
||||||
|
onbefore,
|
||||||
|
onafter,
|
||||||
|
ondestroy: null, // Intentionally not tested, since it will be removed soon
|
||||||
|
onpromiseResolve
|
||||||
|
});
|
||||||
|
hooks.enable();
|
||||||
|
|
||||||
|
function oninit(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
if (type === 'PROMISE') {
|
||||||
|
promisesInitState.set(asyncId, 'inited');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onbefore(asyncId) {
|
||||||
|
if (!promisesInitState.has(asyncId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promisesExecutionState.set(asyncId, 'before');
|
||||||
|
}
|
||||||
|
|
||||||
|
function onafter(asyncId) {
|
||||||
|
if (!promisesInitState.has(asyncId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.strictEqual(promisesExecutionState.get(asyncId), 'before',
|
||||||
|
'after hook called for promise without prior call' +
|
||||||
|
'to before hook');
|
||||||
|
assert.strictEqual(promisesInitState.get(asyncId), 'resolved',
|
||||||
|
'after hook called for promise without prior call' +
|
||||||
|
'to resolve hook');
|
||||||
|
promisesExecutionState.set(asyncId, 'after');
|
||||||
|
}
|
||||||
|
|
||||||
|
function onpromiseResolve(asyncId) {
|
||||||
|
assert(promisesInitState.has(asyncId),
|
||||||
|
'resolve hook called for promise without prior call to init hook');
|
||||||
|
|
||||||
|
promisesInitState.set(asyncId, 'resolved');
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeout = common.platformTimeout(10);
|
||||||
|
|
||||||
|
function checkPromisesInitState() {
|
||||||
|
for (const initState of promisesInitState.values()) {
|
||||||
|
assert.strictEqual(initState, 'resolved',
|
||||||
|
'promise initialized without being resolved');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPromisesExecutionState() {
|
||||||
|
for (const executionState of promisesExecutionState.values()) {
|
||||||
|
assert.strictEqual(executionState, 'after',
|
||||||
|
'mismatch between before and after hook calls');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('beforeExit', common.mustCall(() => {
|
||||||
|
hooks.disable();
|
||||||
|
hooks.sanityCheck('PROMISE');
|
||||||
|
|
||||||
|
checkPromisesInitState();
|
||||||
|
checkPromisesExecutionState();
|
||||||
|
}));
|
||||||
|
|
||||||
|
async function asyncFunc() {
|
||||||
|
await sleep(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncFunc();
|
Loading…
x
Reference in New Issue
Block a user