Skip to content

Commit a7ada43

Browse files
committed
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 <joyeec9h3@gmail.com> PR-URL: nodejs#30467 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
1 parent 0547551 commit a7ada43

6 files changed

Lines changed: 183 additions & 21 deletions

File tree

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ coverage-report-js:
278278
# Runs the C++ tests using the built `cctest` executable.
279279
cctest: all
280280
@out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER)
281-
@out/$(BUILDTYPE)/embedtest "require('./test/embedding/test.js')"
281+
@out/$(BUILDTYPE)/embedtest "require('./test/embedding/test-embedding.js')"
282282

283283
.PHONY: list-gtests
284284
list-gtests:
@@ -534,7 +534,7 @@ test-ci: | clear-stalled bench-addons-build build-addons build-js-native-api-tes
534534
$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
535535
--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
536536
$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
537-
out/Release/embedtest 'require("./test/embedding/test.js")'
537+
out/Release/embedtest 'require("./test/embedding/test-embedding.js")'
538538
@echo "Clean up any leftover processes, error if found."
539539
ps awwx | grep Release/node | grep -v grep | cat
540540
@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \

test/cctest/test_environment.cc

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,144 @@ TEST_F(EnvironmentTest, SetImmediateCleanup) {
329329
EXPECT_EQ(called, 1);
330330
EXPECT_EQ(called_unref, 0);
331331
}
332+
333+
#if HAVE_INSPECTOR
334+
TEST_F(EnvironmentTest, InspectorMultipleEmbeddedEnvironments) {
335+
// Tests that child Environments can be created through the public API
336+
// that are accessible by the inspector.
337+
// This test sets a global variable in the child Environment, and reads it
338+
// back both through the inspector and inside the child Environment, and
339+
// makes sure that those correspond to the value that was originally set.
340+
const v8::HandleScope handle_scope(isolate_);
341+
const Argv argv;
342+
Env env {handle_scope, argv};
343+
344+
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
345+
node::LoadEnvironment(*env,
346+
"'use strict';\n"
347+
"const { Worker } = require('worker_threads');\n"
348+
"const { Session } = require('inspector');\n"
349+
350+
"const session = new Session();\n"
351+
"session.connect();\n"
352+
"session.on('NodeWorker.attachedToWorker', (\n"
353+
" ({ params: { workerInfo, sessionId } }) => {\n"
354+
" session.post('NodeWorker.sendMessageToWorker', {\n"
355+
" sessionId,\n"
356+
" message: JSON.stringify({\n"
357+
" id: 1,\n"
358+
" method: 'Runtime.evaluate',\n"
359+
" params: {\n"
360+
" expression: 'global.variableFromParent = 42;'\n"
361+
" }\n"
362+
" })\n"
363+
" });\n"
364+
" session.on('NodeWorker.receivedMessageFromWorker',\n"
365+
" ({ params: { message } }) => {\n"
366+
" global.messageFromWorker = \n"
367+
" JSON.parse(message).result.result.value;\n"
368+
" });\n"
369+
" }));\n"
370+
"session.post('NodeWorker.enable', { waitForDebuggerOnStart: false });\n")
371+
.ToLocalChecked();
372+
373+
struct ChildEnvironmentData {
374+
node::ThreadId thread_id;
375+
std::unique_ptr<node::InspectorParentHandle> inspector_parent_handle;
376+
node::MultiIsolatePlatform* platform;
377+
int32_t extracted_value = -1;
378+
uv_async_t thread_stopped_async;
379+
};
380+
381+
ChildEnvironmentData data;
382+
data.thread_id = node::AllocateEnvironmentThreadId();
383+
data.inspector_parent_handle =
384+
GetInspectorParentHandle(*env, data.thread_id, "file:///embedded.js");
385+
CHECK(data.inspector_parent_handle);
386+
data.platform = GetMultiIsolatePlatform(*env);
387+
CHECK_NOT_NULL(data.platform);
388+
389+
bool thread_stopped = false;
390+
int err = uv_async_init(
391+
&current_loop, &data.thread_stopped_async, [](uv_async_t* async) {
392+
*static_cast<bool*>(async->data) = true;
393+
uv_close(reinterpret_cast<uv_handle_t*>(async), nullptr);
394+
});
395+
CHECK_EQ(err, 0);
396+
data.thread_stopped_async.data = &thread_stopped;
397+
398+
uv_thread_t thread;
399+
err = uv_thread_create(&thread, [](void* arg) {
400+
ChildEnvironmentData* data = static_cast<ChildEnvironmentData*>(arg);
401+
std::shared_ptr<node::ArrayBufferAllocator> aba =
402+
node::ArrayBufferAllocator::Create();
403+
uv_loop_t loop;
404+
uv_loop_init(&loop);
405+
v8::Isolate* isolate = NewIsolate(aba, &loop, data->platform);
406+
CHECK_NOT_NULL(isolate);
407+
408+
{
409+
v8::Isolate::Scope isolate_scope(isolate);
410+
v8::HandleScope handle_scope(isolate);
411+
412+
v8::Local<v8::Context> context = node::NewContext(isolate);
413+
CHECK(!context.IsEmpty());
414+
v8::Context::Scope context_scope(context);
415+
416+
node::IsolateData* isolate_data = node::CreateIsolateData(
417+
isolate,
418+
&loop,
419+
data->platform);
420+
CHECK_NOT_NULL(isolate_data);
421+
node::Environment* environment = node::CreateEnvironment(
422+
isolate_data,
423+
context,
424+
{ "dummy" },
425+
{},
426+
node::EnvironmentFlags::kNoFlags,
427+
data->thread_id);
428+
CHECK_NOT_NULL(environment);
429+
430+
v8::Local<v8::Value> extracted_value = LoadEnvironment(
431+
environment,
432+
"return global.variableFromParent;",
433+
std::move(data->inspector_parent_handle)).ToLocalChecked();
434+
435+
uv_run(&loop, UV_RUN_DEFAULT);
436+
CHECK(extracted_value->IsInt32());
437+
data->extracted_value = extracted_value.As<v8::Int32>()->Value();
438+
439+
node::FreeEnvironment(environment);
440+
node::FreeIsolateData(isolate_data);
441+
}
442+
443+
data->platform->UnregisterIsolate(isolate);
444+
isolate->Dispose();
445+
uv_run(&loop, UV_RUN_DEFAULT);
446+
CHECK_EQ(uv_loop_close(&loop), 0);
447+
448+
uv_async_send(&data->thread_stopped_async);
449+
}, &data);
450+
CHECK_EQ(err, 0);
451+
452+
bool more;
453+
do {
454+
uv_run(&current_loop, UV_RUN_DEFAULT);
455+
data.platform->DrainTasks(isolate_);
456+
more = uv_loop_alive(&current_loop);
457+
} while (!thread_stopped || more);
458+
459+
uv_thread_join(&thread);
460+
461+
v8::Local<v8::Value> from_inspector =
462+
context->Global()->Get(
463+
context,
464+
v8::String::NewFromOneByte(
465+
isolate_,
466+
reinterpret_cast<const uint8_t*>("messageFromWorker"),
467+
v8::NewStringType::kNormal).ToLocalChecked())
468+
.ToLocalChecked();
469+
CHECK_EQ(data.extracted_value, 42);
470+
CHECK_EQ(from_inspector->IntegerValue(context).FromJust(), 42);
471+
}
472+
#endif // HAVE_INSPECTOR

test/embedding/test-embedding.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
const common = require('../common');
3+
const fixtures = require('../common/fixtures');
4+
const assert = require('assert');
5+
const child_process = require('child_process');
6+
const path = require('path');
7+
8+
common.allowGlobals(global.require);
9+
let binary = process.features.debug ?
10+
'out/Debug/embedtest' : 'out/Release/embedtest';
11+
if (common.isWindows) {
12+
binary += '.exe';
13+
}
14+
binary = path.resolve(__dirname, '..', '..', binary);
15+
16+
assert.strictEqual(
17+
child_process.spawnSync(binary, ['console.log(42)'])
18+
.stdout.toString().trim(),
19+
'42');
20+
21+
assert.strictEqual(
22+
child_process.spawnSync(binary, ['throw new Error()']).status,
23+
1);
24+
25+
assert.strictEqual(
26+
child_process.spawnSync(binary, ['process.exitCode = 8']).status,
27+
8);
28+
29+
30+
const fixturePath = JSON.stringify(fixtures.path('exit.js'));
31+
assert.strictEqual(
32+
child_process.spawnSync(binary, [`require(${fixturePath})`, 92]).status,
33+
92);

test/embedding/test.js

Lines changed: 0 additions & 19 deletions
This file was deleted.

test/embedding/testcfg.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import sys, os
2+
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
3+
import testpy
4+
5+
def GetConfiguration(context, root):
6+
return testpy.SimpleTestConfiguration(context, root, 'embedding')

tools/test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,7 @@ def PrintCrashed(code):
14861486
'addons',
14871487
'benchmark',
14881488
'doctool',
1489+
'embedding',
14891490
'internet',
14901491
'js-native-api',
14911492
'node-api',

0 commit comments

Comments
 (0)