Skip to content

Commit

Permalink
test: add first set of func Ref tests
Browse files Browse the repository at this point in the history
PR-URL: nodejs/node-addon-api#1035
Reviewed-By: Michael Dawson <midawson@redhat.com
Reviewed-By: Kevin Eady <kevin.c.eady@gmail.com>
  • Loading branch information
Marlyfleitas committed Oct 25, 2021
1 parent 748a7d4 commit 2dc3c96
Show file tree
Hide file tree
Showing 2 changed files with 314 additions and 6 deletions.
173 changes: 172 additions & 1 deletion test/function_reference.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,163 @@

using namespace Napi;

class FuncRefObject : public Napi::ObjectWrap<FuncRefObject> {
public:
FuncRefObject(const Napi::CallbackInfo& info)
: Napi::ObjectWrap<FuncRefObject>(info) {
Napi::Env env = info.Env();
int argLen = info.Length();
if (argLen <= 0 || !info[0].IsNumber()) {
Napi::TypeError::New(env, "First param should be a number")
.ThrowAsJavaScriptException();
return;
}
Napi::Number value = info[0].As<Napi::Number>();
this->_value = value.Int32Value();
}

Napi::Value GetValue(const Napi::CallbackInfo& info) {
int value = this->_value;
return Napi::Number::New(info.Env(), value);
}

private:
int _value;
};

namespace {

Value ConstructRefFromExisitingRef(const CallbackInfo& info) {
HandleScope scope(info.Env());
FunctionReference ref;
FunctionReference movedRef;
ref.Reset(info[0].As<Function>());
movedRef = std::move(ref);

return MaybeUnwrap(movedRef({}));
}

Value CallWithVectorArgs(const CallbackInfo& info) {
HandleScope scope(info.Env());
std::vector<napi_value> newVec;
FunctionReference ref;
ref.Reset(info[0].As<Function>());

for (int i = 1; i < (int)info.Length(); i++) {
newVec.push_back(info[i]);
}
return MaybeUnwrap(ref.Call(newVec));
}

Value CallWithInitList(const CallbackInfo& info) {
HandleScope scope(info.Env());
FunctionReference ref;
ref.Reset(info[0].As<Function>());

return MaybeUnwrap(ref.Call({info[1], info[2], info[3]}));
}

Value CallWithRecvInitList(const CallbackInfo& info) {
HandleScope scope(info.Env());
FunctionReference ref;
ref.Reset(info[0].As<Function>());

return MaybeUnwrap(ref.Call(info[1], {info[2], info[3], info[4]}));
}

Value CallWithRecvVector(const CallbackInfo& info) {
HandleScope scope(info.Env());
FunctionReference ref;
std::vector<napi_value> newVec;
ref.Reset(info[0].As<Function>());

for (int i = 2; i < (int)info.Length(); i++) {
newVec.push_back(info[i]);
}
return MaybeUnwrap(ref.Call(info[1], newVec));
}

Value CallWithRecvArgc(const CallbackInfo& info) {
HandleScope scope(info.Env());
FunctionReference ref;
int argLength = info.Length() - 2;
napi_value* args = new napi_value[argLength];
ref.Reset(info[0].As<Function>());

int argIdx = 0;
for (int i = 2; i < (int)info.Length(); i++, argIdx++) {
args[argIdx] = info[i];
}

return MaybeUnwrap(ref.Call(info[1], argLength, args));
}

Value MakeAsyncCallbackWithInitList(const Napi::CallbackInfo& info) {
Napi::FunctionReference ref;
ref.Reset(info[0].As<Function>());

Napi::AsyncContext context(info.Env(), "func_ref_resources", {});

return MaybeUnwrap(
ref.MakeCallback(Napi::Object::New(info.Env()), {}, context));
}

Value MakeAsyncCallbackWithVector(const Napi::CallbackInfo& info) {
Napi::FunctionReference ref;
ref.Reset(info[0].As<Function>());
std::vector<napi_value> newVec;
Napi::AsyncContext context(info.Env(), "func_ref_resources", {});

for (int i = 1; i < (int)info.Length(); i++) {
newVec.push_back(info[i]);
}

return MaybeUnwrap(
ref.MakeCallback(Napi::Object::New(info.Env()), newVec, context));
}

Value MakeAsyncCallbackWithArgv(const Napi::CallbackInfo& info) {
Napi::FunctionReference ref;
ref.Reset(info[0].As<Function>());
int argLength = info.Length() - 1;
napi_value* args = new napi_value[argLength];

int argIdx = 0;
for (int i = 1; i < (int)info.Length(); i++, argIdx++) {
args[argIdx] = info[i];
}

Napi::AsyncContext context(info.Env(), "func_ref_resources", {});
return MaybeUnwrap(ref.MakeCallback(
Napi::Object::New(info.Env()), argLength, args, context));
}

Value CreateFunctionReferenceUsingNew(const Napi::CallbackInfo& info) {
Napi::Function func = ObjectWrap<FuncRefObject>::DefineClass(
info.Env(),
"MyObject",
{ObjectWrap<FuncRefObject>::InstanceMethod("getValue",
&FuncRefObject::GetValue)});
Napi::FunctionReference* constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(func);

return MaybeUnwrapOr(constructor->New({info[0].As<Number>()}), Object());
}

Value CreateFunctionReferenceUsingNewVec(const Napi::CallbackInfo& info) {
Napi::Function func = ObjectWrap<FuncRefObject>::DefineClass(
info.Env(),
"MyObject",
{ObjectWrap<FuncRefObject>::InstanceMethod("getValue",
&FuncRefObject::GetValue)});
Napi::FunctionReference* constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(func);
std::vector<napi_value> newVec;
newVec.push_back(info[0]);

return MaybeUnwrapOr(constructor->New(newVec), Object());
}

Value Call(const CallbackInfo& info) {
HandleScope scope(info.Env());
FunctionReference ref;
Expand All @@ -23,7 +179,22 @@ Value Construct(const CallbackInfo& info) {

Object InitFunctionReference(Env env) {
Object exports = Object::New(env);

exports["CreateFuncRefWithNew"] =
Function::New(env, CreateFunctionReferenceUsingNew);
exports["CreateFuncRefWithNewVec"] =
Function::New(env, CreateFunctionReferenceUsingNewVec);
exports["CallWithRecvArgc"] = Function::New(env, CallWithRecvArgc);
exports["CallWithRecvVector"] = Function::New(env, CallWithRecvVector);
exports["CallWithRecvInitList"] = Function::New(env, CallWithRecvInitList);
exports["CallWithInitList"] = Function::New(env, CallWithInitList);
exports["CallWithVec"] = Function::New(env, CallWithVectorArgs);
exports["ConstructWithMove"] =
Function::New(env, ConstructRefFromExisitingRef);
exports["AsyncCallWithInitList"] =
Function::New(env, MakeAsyncCallbackWithInitList);
exports["AsyncCallWithVector"] =
Function::New(env, MakeAsyncCallbackWithVector);
exports["AsyncCallWithArgv"] = Function::New(env, MakeAsyncCallbackWithArgv);
exports["call"] = Function::New(env, Call);
exports["construct"] = Function::New(env, Construct);

Expand Down
147 changes: 142 additions & 5 deletions test/function_reference.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,157 @@
'use strict';

const assert = require('assert');
const asyncHook = require('async_hooks');

module.exports = require('./common').runTest(binding => {
test(binding.functionreference);
module.exports = require('./common').runTest(async (binding) => {
await test(binding.functionreference);
});

function test(binding) {
function installAsyncHook () {
let id;
let destroyed;
let hook;
const events = [];
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (destroyed) {
hook.disable();
clearInterval(interval);
resolve(events);
}
}, 10);

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

function canConstructRefFromExistingRef (binding) {
const testFunc = () => 240;
assert(binding.ConstructWithMove(testFunc) === 240);
}

function canCallFunctionWithDifferentOverloads (binding) {
let outsideRef = {};
const testFunc = (a, b) => a * a - b * b;
const testFuncB = (a, b, c) => a + b - c * c;
const testFuncC = (a, b, c) => {
outsideRef.a = a;
outsideRef.b = b;
outsideRef.c = c;
};
const testFuncD = (a, b, c, d) => {
outsideRef.result = a + b * c - d;
return outsideRef.result;
};

assert(binding.CallWithVec(testFunc, 5, 4) === testFunc(5, 4));
assert(binding.CallWithInitList(testFuncB, 2, 4, 5) === testFuncB(2, 4, 5));

binding.CallWithRecvVector(testFuncC, outsideRef, 1, 2, 4);
assert(outsideRef.a === 1 && outsideRef.b === 2 && outsideRef.c === 4);

outsideRef = {};
binding.CallWithRecvInitList(testFuncC, outsideRef, 1, 2, 4);
assert(outsideRef.a === 1 && outsideRef.b === 2 && outsideRef.c === 4);

outsideRef = {};
binding.CallWithRecvArgc(testFuncD, outsideRef, 2, 4, 5, 6);
assert(outsideRef.result === testFuncD(2, 4, 5, 6));
}

async function canCallAsyncFunctionWithDifferentOverloads (binding) {
const testFunc = () => 2100;
const testFuncB = (a, b, c, d) => a + b + c + d;
let hook = installAsyncHook();
binding.AsyncCallWithInitList(testFunc);
let triggerAsyncId = asyncHook.executionAsyncId();
let res = await hook;
assert.deepStrictEqual(res, [
{
eventName: 'init',
type: 'func_ref_resources',
triggerAsyncId: triggerAsyncId,
resource: {}
},
{ eventName: 'before' },
{ eventName: 'after' },
{ eventName: 'destroy' }
]);

hook = installAsyncHook();
triggerAsyncId = asyncHook.executionAsyncId();
assert(
binding.AsyncCallWithVector(testFuncB, 2, 4, 5, 6) === testFuncB(2, 4, 5, 6)
);
res = await hook;
assert.deepStrictEqual(res, [
{
eventName: 'init',
type: 'func_ref_resources',
triggerAsyncId: triggerAsyncId,
resource: {}
},
{ eventName: 'before' },
{ eventName: 'after' },
{ eventName: 'destroy' }
]);

hook = installAsyncHook();
triggerAsyncId = asyncHook.executionAsyncId();
assert(
binding.AsyncCallWithArgv(testFuncB, 2, 4, 5, 6) === testFuncB(2, 4, 5, 6)
);
}
async function test (binding) {
const e = new Error('foobar');
const functionMayThrow = () => { throw e; };
const classMayThrow = class { constructor() { throw e; } };
const functionMayThrow = () => {
throw e;
};
const classMayThrow = class {
constructor () {
throw e;
}
};

const newRef = binding.CreateFuncRefWithNew(120);
assert(newRef.getValue() === 120);

const newRefWithVecArg = binding.CreateFuncRefWithNewVec(80);
assert(newRefWithVecArg.getValue() === 80);

assert.throws(() => {
binding.call(functionMayThrow);
}, /foobar/);
assert.throws(() => {
binding.construct(classMayThrow);
}, /foobar/);

canConstructRefFromExistingRef(binding);
canCallFunctionWithDifferentOverloads(binding);
await canCallAsyncFunctionWithDifferentOverloads(binding);
}

0 comments on commit 2dc3c96

Please sign in to comment.