Skip to content

Commit

Permalink
Add test coverage for async contexts (#1164)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevindavies8 committed Apr 29, 2022
1 parent 8a90214 commit eb6ae0e
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 33 deletions.
15 changes: 15 additions & 0 deletions test/async_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,25 @@ static void MakeCallback(const CallbackInfo& info) {
Object::New(info.Env()), std::initializer_list<napi_value>{}, context);
}

static void MakeCallbackNoResource(const CallbackInfo& info) {
Function callback = info[0].As<Function>();
AsyncContext context(info.Env(), "async_context_no_res_test");
callback.MakeCallback(
Object::New(info.Env()), std::initializer_list<napi_value>{}, context);
}

static Boolean AssertAsyncContextReturnCorrectEnv(const CallbackInfo& info) {
AsyncContext context(info.Env(), "empty_context_test");
return Boolean::New(info.Env(), context.Env() == info.Env());
}
} // end anonymous namespace

Object InitAsyncContext(Env env) {
Object exports = Object::New(env);
exports["makeCallback"] = Function::New(env, MakeCallback);
exports["makeCallbackNoResource"] =
Function::New(env, MakeCallbackNoResource);
exports["asyncCxtReturnCorrectEnv"] =
Function::New(env, AssertAsyncContextReturnCorrectEnv);
return exports;
}
102 changes: 69 additions & 33 deletions test/async_context.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ const common = require('./common');

// we only check async hooks on 8.x an higher were
// they are closer to working properly
const nodeVersion = process.versions.node.split('.')[0]
let async_hooks = undefined;
function checkAsyncHooks() {
const nodeVersion = process.versions.node.split('.')[0];
let asyncHooks;
function checkAsyncHooks () {
if (nodeVersion >= 8) {
if (async_hooks == undefined) {
async_hooks = require('async_hooks');
if (asyncHooks === undefined) {
asyncHooks = require('async_hooks');
}
return true;
}
Expand All @@ -19,68 +19,104 @@ function checkAsyncHooks() {

module.exports = common.runTest(test);

function installAsyncHooksForTest() {
function installAsyncHooksForTest (resName) {
return new Promise((resolve, reject) => {
let id;
const events = [];
/**
* TODO(legendecas): investigate why resolving & disabling hooks in
* destroy callback causing crash with case 'callbackscope.js'.
*/
let hook;
let destroyed = false;
const interval = setInterval(() => {
if (destroyed) {
hook.disable();
clearInterval(interval);
resolve(events);
}
}, 10);

hook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (id === undefined && type === 'async_context_test') {
const hook = asyncHooks.createHook({
init (asyncId, type, triggerAsyncId, resource) {
if (id === undefined && type === resName) {
id = asyncId;
events.push({ eventName: 'init', type, triggerAsyncId, resource });
}
},
before(asyncId) {
before (asyncId) {
if (asyncId === id) {
events.push({ eventName: 'before' });
}
},
after(asyncId) {
after (asyncId) {
if (asyncId === id) {
events.push({ eventName: 'after' });
}
},
destroy(asyncId) {
destroy (asyncId) {
if (asyncId === id) {
events.push({ eventName: 'destroy' });
destroyed = true;
}
}
}).enable();

const interval = setInterval(() => {
if (destroyed) {
hook.disable();
clearInterval(interval);
resolve(events);
}
}, 10);
});
}

function test(binding) {
if (!checkAsyncHooks()) {
return;
}

const hooks = installAsyncHooksForTest();
const triggerAsyncId = async_hooks.executionAsyncId();
binding.asynccontext.makeCallback(common.mustCall(), { foo: 'foo' });
return hooks.then(actual => {
async function makeCallbackWithResource (binding) {
const hooks = installAsyncHooksForTest('async_context_test');
const triggerAsyncId = asyncHooks.executionAsyncId();
await new Promise((resolve, reject) => {
binding.asynccontext.makeCallback(common.mustCall(), { foo: 'foo' });
hooks.then(actual => {
assert.deepStrictEqual(actual, [
{ eventName: 'init',
{
eventName: 'init',
type: 'async_context_test',
triggerAsyncId: triggerAsyncId,
resource: { foo: 'foo' } },
resource: { foo: 'foo' }
},
{ eventName: 'before' },
{ eventName: 'after' },
{ eventName: 'destroy' }
]);
}).catch(common.mustNotCall());
resolve();
});
}

async function makeCallbackWithoutResource (binding) {
const hooks = installAsyncHooksForTest('async_context_no_res_test');
const triggerAsyncId = asyncHooks.executionAsyncId();
await new Promise((resolve, reject) => {
binding.asynccontext.makeCallbackNoResource(common.mustCall());
hooks.then(actual => {
assert.deepStrictEqual(actual, [
{
eventName: 'init',
type: 'async_context_no_res_test',
triggerAsyncId: triggerAsyncId,
resource: { }
},
{ eventName: 'before' },
{ eventName: 'after' },
{ eventName: 'destroy' }
]);
}).catch(common.mustNotCall());
}).catch(common.mustNotCall());
resolve();
});
}

function assertAsyncContextReturnsCorrectEnv (binding) {
assert.strictEqual(binding.asynccontext.asyncCxtReturnCorrectEnv(), true);
}

async function test (binding) {
if (!checkAsyncHooks()) {
return;
}

await makeCallbackWithResource(binding);
await makeCallbackWithoutResource(binding);
assertAsyncContextReturnsCorrectEnv(binding);
}

0 comments on commit eb6ae0e

Please sign in to comment.