From 2dc3c96fc480480c588c2c7a13c31a8adb1780f3 Mon Sep 17 00:00:00 2001 From: Marly Fleitas Date: Mon, 9 Aug 2021 20:54:19 -0400 Subject: [PATCH] test: add first set of func Ref tests PR-URL: https://github.com/nodejs/node-addon-api/pull/1035 Reviewed-By: Michael Dawson --- test/function_reference.cc | 173 ++++++++++++++++++++++++++++++++++++- test/function_reference.js | 147 +++++++++++++++++++++++++++++-- 2 files changed, 314 insertions(+), 6 deletions(-) diff --git a/test/function_reference.cc b/test/function_reference.cc index aaa899e..a251198 100644 --- a/test/function_reference.cc +++ b/test/function_reference.cc @@ -3,7 +3,163 @@ using namespace Napi; +class FuncRefObject : public Napi::ObjectWrap { + public: + FuncRefObject(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(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(); + 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()); + movedRef = std::move(ref); + + return MaybeUnwrap(movedRef({})); +} + +Value CallWithVectorArgs(const CallbackInfo& info) { + HandleScope scope(info.Env()); + std::vector newVec; + FunctionReference ref; + ref.Reset(info[0].As()); + + 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()); + + 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()); + + 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 newVec; + ref.Reset(info[0].As()); + + 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()); + + 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()); + + 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()); + std::vector 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()); + 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::DefineClass( + info.Env(), + "MyObject", + {ObjectWrap::InstanceMethod("getValue", + &FuncRefObject::GetValue)}); + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + + return MaybeUnwrapOr(constructor->New({info[0].As()}), Object()); +} + +Value CreateFunctionReferenceUsingNewVec(const Napi::CallbackInfo& info) { + Napi::Function func = ObjectWrap::DefineClass( + info.Env(), + "MyObject", + {ObjectWrap::InstanceMethod("getValue", + &FuncRefObject::GetValue)}); + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + std::vector newVec; + newVec.push_back(info[0]); + + return MaybeUnwrapOr(constructor->New(newVec), Object()); +} + Value Call(const CallbackInfo& info) { HandleScope scope(info.Env()); FunctionReference ref; @@ -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); diff --git a/test/function_reference.js b/test/function_reference.js index 3266f00..84263fc 100644 --- a/test/function_reference.js +++ b/test/function_reference.js @@ -1,15 +1,148 @@ '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); @@ -17,4 +150,8 @@ function test(binding) { assert.throws(() => { binding.construct(classMayThrow); }, /foobar/); + + canConstructRefFromExistingRef(binding); + canCallFunctionWithDifferentOverloads(binding); + await canCallAsyncFunctionWithDifferentOverloads(binding); }