diff options
Diffstat (limited to 'test/addons-napi')
-rw-r--r-- | test/addons-napi/test_callback_scope/binding.cc | 138 | ||||
-rw-r--r-- | test/addons-napi/test_callback_scope/binding.gyp | 9 | ||||
-rw-r--r-- | test/addons-napi/test_callback_scope/test-async-hooks.js | 29 | ||||
-rw-r--r-- | test/addons-napi/test_callback_scope/test-resolve-async.js | 13 | ||||
-rw-r--r-- | test/addons-napi/test_callback_scope/test.js | 17 |
5 files changed, 206 insertions, 0 deletions
diff --git a/test/addons-napi/test_callback_scope/binding.cc b/test/addons-napi/test_callback_scope/binding.cc new file mode 100644 index 0000000000..e6631b6ac7 --- /dev/null +++ b/test/addons-napi/test_callback_scope/binding.cc @@ -0,0 +1,138 @@ +#include "node_api.h" +#include "uv.h" +#include "../common.h" + +namespace { + +// the test needs to fake out the async structure, so we need to use +// the raw structure here and then cast as done behind the scenes +// in napi calls. +struct async_context { + double async_id; + double trigger_async_id; +}; + + +napi_value RunInCallbackScope(napi_env env, napi_callback_info info) { + size_t argc; + napi_value args[4]; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr)); + NAPI_ASSERT(env, argc == 4 , "Wrong number of arguments"); + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_number, + "Wrong type of arguments. Expects a number as second argument."); + + NAPI_CALL(env, napi_typeof(env, args[2], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_number, + "Wrong type of arguments. Expects a number as third argument."); + + NAPI_CALL(env, napi_typeof(env, args[3], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_function, + "Wrong type of arguments. Expects a function as third argument."); + + struct async_context context; + NAPI_CALL(env, napi_get_value_double(env, args[1], &context.async_id)); + NAPI_CALL(env, + napi_get_value_double(env, args[2], &context.trigger_async_id)); + + napi_callback_scope scope = nullptr; + NAPI_CALL( + env, + napi_open_callback_scope(env, + args[0], + reinterpret_cast<napi_async_context>(&context), + &scope)); + + // if the function has an exception pending after the call that is ok + // so we don't use NAPI_CALL as we must close the callback scope regardless + napi_value result = nullptr; + napi_status function_call_result = + napi_call_function(env, args[0], args[3], 0, nullptr, &result); + if (function_call_result != napi_ok) { + GET_AND_THROW_LAST_ERROR((env)); + } + + NAPI_CALL(env, napi_close_callback_scope(env, scope)); + + return result; +} + +static napi_env shared_env = nullptr; +static napi_deferred deferred = nullptr; + +static void Callback(uv_work_t* req, int ignored) { + napi_env env = shared_env; + + napi_handle_scope handle_scope = nullptr; + NAPI_CALL_RETURN_VOID(env, napi_open_handle_scope(env, &handle_scope)); + + napi_value resource_name; + NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8( + env, "test", NAPI_AUTO_LENGTH, &resource_name)); + napi_async_context context; + NAPI_CALL_RETURN_VOID(env, + napi_async_init(env, nullptr, resource_name, &context)); + + napi_value resource_object; + NAPI_CALL_RETURN_VOID(env, napi_create_object(env, &resource_object)); + + napi_value undefined_value; + NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined_value)); + + napi_callback_scope scope = nullptr; + NAPI_CALL_RETURN_VOID(env, napi_open_callback_scope(env, + resource_object, + context, + &scope)); + + NAPI_CALL_RETURN_VOID(env, + napi_resolve_deferred(env, deferred, undefined_value)); + + NAPI_CALL_RETURN_VOID(env, napi_close_callback_scope(env, scope)); + + NAPI_CALL_RETURN_VOID(env, napi_close_handle_scope(env, handle_scope)); + delete req; +} + +napi_value TestResolveAsync(napi_env env, napi_callback_info info) { + napi_value promise = nullptr; + if (deferred == nullptr) { + shared_env = env; + NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); + + uv_loop_t* loop = nullptr; + NAPI_CALL(env, napi_get_uv_event_loop(env, &loop)); + + uv_work_t* req = new uv_work_t(); + uv_queue_work(loop, + req, + [](uv_work_t*) {}, + Callback); + } + return promise; +} + +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("runInCallbackScope", RunInCallbackScope), + DECLARE_NAPI_PROPERTY("testResolveAsync", TestResolveAsync) + }; + + NAPI_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} + +} // anonymous namespace + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_callback_scope/binding.gyp b/test/addons-napi/test_callback_scope/binding.gyp new file mode 100644 index 0000000000..7ede63d94a --- /dev/null +++ b/test/addons-napi/test_callback_scope/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], + 'sources': [ 'binding.cc' ] + } + ] +} diff --git a/test/addons-napi/test_callback_scope/test-async-hooks.js b/test/addons-napi/test_callback_scope/test-async-hooks.js new file mode 100644 index 0000000000..1a11bf6039 --- /dev/null +++ b/test/addons-napi/test_callback_scope/test-async-hooks.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +// The async_hook that we enable would register the process.emitWarning() +// call from loading the N-API addon as asynchronous activity because +// it contains a process.nextTick() call. Monkey patch it to be a no-op +// before we load the addon in order to avoid this. +process.emitWarning = () => {}; + +const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); + +let insideHook = false; +async_hooks.createHook({ + before: common.mustCall((id) => { + assert.strictEqual(id, 1000); + insideHook = true; + }), + after: common.mustCall((id) => { + assert.strictEqual(id, 1000); + insideHook = false; + }) +}).enable(); + +runInCallbackScope({}, 1000, 1000, () => { + assert(insideHook); +}); diff --git a/test/addons-napi/test_callback_scope/test-resolve-async.js b/test/addons-napi/test_callback_scope/test-resolve-async.js new file mode 100644 index 0000000000..e9f4b9044c --- /dev/null +++ b/test/addons-napi/test_callback_scope/test-resolve-async.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); +const { testResolveAsync } = require(`./build/${common.buildType}/binding`); + +let called = false; +testResolveAsync().then(common.mustCall(() => { + called = true; +})); + +setTimeout(common.mustCall(() => { assert(called); }), + common.platformTimeout(20)); diff --git a/test/addons-napi/test_callback_scope/test.js b/test/addons-napi/test_callback_scope/test.js new file mode 100644 index 0000000000..2f2efe5f47 --- /dev/null +++ b/test/addons-napi/test_callback_scope/test.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); +const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); + +assert.strictEqual(runInCallbackScope({}, 0, 0, () => 42), 42); + +{ + process.once('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); + })); + + runInCallbackScope({}, 0, 0, () => { + throw new Error('foo'); + }); +} |