From 2061c336709939fec9549254d6d8b072b3685fbd Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 12 Mar 2020 13:11:48 +0100 Subject: [PATCH] test: add extended embedder cctest Add an embedder cctest that also covers a multi-Environment situation, including worker_threads-style inspector support. Co-authored-by: Joyee Cheung PR-URL: https://github.com/nodejs/node/pull/30467 Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil --- Makefile | 4 +- test/cctest/test_environment.cc | 141 +++++++++++++++++++++++++++++++ test/embedding/test-embedding.js | 33 ++++++++ test/embedding/test.js | 19 ----- test/embedding/testcfg.py | 6 ++ tools/test.py | 1 + 6 files changed, 183 insertions(+), 21 deletions(-) create mode 100644 test/embedding/test-embedding.js delete mode 100644 test/embedding/test.js create mode 100644 test/embedding/testcfg.py diff --git a/Makefile b/Makefile index e144d8b81cec3b..71917bb6143c6d 100644 --- a/Makefile +++ b/Makefile @@ -278,7 +278,7 @@ coverage-report-js: # Runs the C++ tests using the built `cctest` executable. cctest: all @out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER) - @out/$(BUILDTYPE)/embedtest "require('./test/embedding/test.js')" + @out/$(BUILDTYPE)/embedtest "require('./test/embedding/test-embedding.js')" .PHONY: list-gtests list-gtests: @@ -532,7 +532,7 @@ test-ci: | clear-stalled build-addons build-js-native-api-tests build-node-api-t $(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \ --mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \ $(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC) - out/Release/embedtest 'require("./test/embedding/test.js")' + out/Release/embedtest 'require("./test/embedding/test-embedding.js")' @echo "Clean up any leftover processes, error if found." ps awwx | grep Release/node | grep -v grep | cat @PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \ diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index 4d8237f103a07e..23b37ee3c60ae4 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -306,3 +306,144 @@ TEST_F(EnvironmentTest, BufferWithFreeCallbackIsDetached) { CHECK_EQ(callback_calls, 1); CHECK_EQ(ab->ByteLength(), 0); } + +#if HAVE_INSPECTOR +TEST_F(EnvironmentTest, InspectorMultipleEmbeddedEnvironments) { + // Tests that child Environments can be created through the public API + // that are accessible by the inspector. + // This test sets a global variable in the child Environment, and reads it + // back both through the inspector and inside the child Environment, and + // makes sure that those correspond to the value that was originally set. + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env {handle_scope, argv}; + + v8::Local context = isolate_->GetCurrentContext(); + node::LoadEnvironment(*env, + "'use strict';\n" + "const { Worker } = require('worker_threads');\n" + "const { Session } = require('inspector');\n" + + "const session = new Session();\n" + "session.connect();\n" + "session.on('NodeWorker.attachedToWorker', (\n" + " ({ params: { workerInfo, sessionId } }) => {\n" + " session.post('NodeWorker.sendMessageToWorker', {\n" + " sessionId,\n" + " message: JSON.stringify({\n" + " id: 1,\n" + " method: 'Runtime.evaluate',\n" + " params: {\n" + " expression: 'global.variableFromParent = 42;'\n" + " }\n" + " })\n" + " });\n" + " session.on('NodeWorker.receivedMessageFromWorker',\n" + " ({ params: { message } }) => {\n" + " global.messageFromWorker = \n" + " JSON.parse(message).result.result.value;\n" + " });\n" + " }));\n" + "session.post('NodeWorker.enable', { waitForDebuggerOnStart: false });\n") + .ToLocalChecked(); + + struct ChildEnvironmentData { + node::ThreadId thread_id; + std::unique_ptr inspector_parent_handle; + node::MultiIsolatePlatform* platform; + int32_t extracted_value = -1; + uv_async_t thread_stopped_async; + }; + + ChildEnvironmentData data; + data.thread_id = node::AllocateEnvironmentThreadId(); + data.inspector_parent_handle = + GetInspectorParentHandle(*env, data.thread_id, "file:///embedded.js"); + CHECK(data.inspector_parent_handle); + data.platform = GetMultiIsolatePlatform(*env); + CHECK_NOT_NULL(data.platform); + + bool thread_stopped = false; + int err = uv_async_init( + ¤t_loop, &data.thread_stopped_async, [](uv_async_t* async) { + *static_cast(async->data) = true; + uv_close(reinterpret_cast(async), nullptr); + }); + CHECK_EQ(err, 0); + data.thread_stopped_async.data = &thread_stopped; + + uv_thread_t thread; + err = uv_thread_create(&thread, [](void* arg) { + ChildEnvironmentData* data = static_cast(arg); + std::shared_ptr aba = + node::ArrayBufferAllocator::Create(); + uv_loop_t loop; + uv_loop_init(&loop); + v8::Isolate* isolate = NewIsolate(aba, &loop, data->platform); + CHECK_NOT_NULL(isolate); + + { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + v8::Local context = node::NewContext(isolate); + CHECK(!context.IsEmpty()); + v8::Context::Scope context_scope(context); + + node::IsolateData* isolate_data = node::CreateIsolateData( + isolate, + &loop, + data->platform); + CHECK_NOT_NULL(isolate_data); + node::Environment* environment = node::CreateEnvironment( + isolate_data, + context, + { "dummy" }, + {}, + node::EnvironmentFlags::kNoFlags, + data->thread_id); + CHECK_NOT_NULL(environment); + + v8::Local extracted_value = LoadEnvironment( + environment, + "return global.variableFromParent;", + std::move(data->inspector_parent_handle)).ToLocalChecked(); + + uv_run(&loop, UV_RUN_DEFAULT); + CHECK(extracted_value->IsInt32()); + data->extracted_value = extracted_value.As()->Value(); + + node::FreeEnvironment(environment); + node::FreeIsolateData(isolate_data); + } + + data->platform->UnregisterIsolate(isolate); + isolate->Dispose(); + uv_run(&loop, UV_RUN_DEFAULT); + CHECK_EQ(uv_loop_close(&loop), 0); + + uv_async_send(&data->thread_stopped_async); + }, &data); + CHECK_EQ(err, 0); + + bool more; + do { + uv_run(¤t_loop, UV_RUN_DEFAULT); + data.platform->DrainTasks(isolate_); + more = uv_loop_alive(¤t_loop); + } while (!thread_stopped || more); + + uv_thread_join(&thread); + + v8::Local from_inspector = + context->Global()->Get( + context, + v8::String::NewFromOneByte( + isolate_, + reinterpret_cast("messageFromWorker"), + v8::NewStringType::kNormal).ToLocalChecked()) + .ToLocalChecked(); + CHECK_EQ(data.extracted_value, 42); + CHECK_EQ(from_inspector->IntegerValue(context).FromJust(), 42); +} +#endif // HAVE_INSPECTOR diff --git a/test/embedding/test-embedding.js b/test/embedding/test-embedding.js new file mode 100644 index 00000000000000..43dab0ab24ea53 --- /dev/null +++ b/test/embedding/test-embedding.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const child_process = require('child_process'); +const path = require('path'); + +common.allowGlobals(global.require); +let binary = process.features.debug ? + 'out/Debug/embedtest' : 'out/Release/embedtest'; +if (common.isWindows) { + binary += '.exe'; +} +binary = path.resolve(__dirname, '..', '..', binary); + +assert.strictEqual( + child_process.spawnSync(binary, ['console.log(42)']) + .stdout.toString().trim(), + '42'); + +assert.strictEqual( + child_process.spawnSync(binary, ['throw new Error()']).status, + 1); + +assert.strictEqual( + child_process.spawnSync(binary, ['process.exitCode = 8']).status, + 8); + + +const fixturePath = JSON.stringify(fixtures.path('exit.js')); +assert.strictEqual( + child_process.spawnSync(binary, [`require(${fixturePath})`, 92]).status, + 92); diff --git a/test/embedding/test.js b/test/embedding/test.js deleted file mode 100644 index a802de1849021f..00000000000000 --- a/test/embedding/test.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; -const common = require('../common'); -const assert = require('assert'); -const child_process = require('child_process'); - -common.allowGlobals(global.require); - -assert.strictEqual( - child_process.spawnSync(process.execPath, ['console.log(42)']) - .stdout.toString().trim(), - '42'); - -assert.strictEqual( - child_process.spawnSync(process.execPath, ['throw new Error()']).status, - 1); - -assert.strictEqual( - child_process.spawnSync(process.execPath, ['process.exitCode = 8']).status, - 8); diff --git a/test/embedding/testcfg.py b/test/embedding/testcfg.py new file mode 100644 index 00000000000000..a4b90f490c670f --- /dev/null +++ b/test/embedding/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.SimpleTestConfiguration(context, root, 'embedding') diff --git a/tools/test.py b/tools/test.py index 112d5edaffaf4f..5eb1fa767219ce 100755 --- a/tools/test.py +++ b/tools/test.py @@ -1491,6 +1491,7 @@ def PrintCrashed(code): 'addons', 'benchmark', 'doctool', + 'embedding', 'internet', 'js-native-api', 'node-api',