Skip to content

Commit 2061c33

Browse files
committedMar 21, 2020
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 <[email protected]> PR-URL: nodejs#30467 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent e04f599 commit 2061c33

File tree

6 files changed

+183
-21
lines changed

6 files changed

+183
-21
lines changed
 

‎Makefile

+2-2
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:
@@ -532,7 +532,7 @@ test-ci: | clear-stalled build-addons build-js-native-api-tests build-node-api-t
532532
$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
533533
--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
534534
$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
535-
out/Release/embedtest 'require("./test/embedding/test.js")'
535+
out/Release/embedtest 'require("./test/embedding/test-embedding.js")'
536536
@echo "Clean up any leftover processes, error if found."
537537
ps awwx | grep Release/node | grep -v grep | cat
538538
@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \

‎test/cctest/test_environment.cc

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

‎test/embedding/test-embedding.js

+33
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

-19
This file was deleted.

‎test/embedding/testcfg.py

+6
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

+1
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,7 @@ def PrintCrashed(code):
14911491
'addons',
14921492
'benchmark',
14931493
'doctool',
1494+
'embedding',
14941495
'internet',
14951496
'js-native-api',
14961497
'node-api',

0 commit comments

Comments
 (0)
Please sign in to comment.