Skip to content

Commit 43d32b0

Browse files
committed
src,test: add full-featured embedder API test
PR-URL: #30467 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
1 parent a8cf886 commit 43d32b0

File tree

6 files changed

+226
-2
lines changed

6 files changed

+226
-2
lines changed

Makefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ coverage-clean:
212212
$(RM) out/$(BUILDTYPE)/obj.target/node/src/tracing/*.gcno
213213
$(RM) out/$(BUILDTYPE)/obj.target/cctest/src/*.gcno
214214
$(RM) out/$(BUILDTYPE)/obj.target/cctest/test/cctest/*.gcno
215+
$(RM) out/$(BUILDTYPE)/obj.target/embedtest/src/*.gcno
216+
$(RM) out/$(BUILDTYPE)/obj.target/embedtest/test/embedding/*.gcno
215217

216218
.PHONY: coverage
217219
# Build and test with code coverage reporting. Leave the lib directory
@@ -250,8 +252,8 @@ coverage-test: coverage-build
250252
TEST_CI_ARGS="$(TEST_CI_ARGS) --type=coverage" $(MAKE) $(COVTESTS)
251253
$(MAKE) coverage-report-js
252254
-(cd out && "../gcovr/scripts/gcovr" \
253-
--gcov-exclude='.*\b(deps|usr|out|cctest)\b' -v -r Release/obj.target \
254-
--html --html-detail -o ../coverage/cxxcoverage.html \
255+
--gcov-exclude='.*\b(deps|usr|out|cctest|embedding)\b' -v \
256+
-r Release/obj.target --html --html-detail -o ../coverage/cxxcoverage.html \
255257
--gcov-executable="$(GCOV)")
256258
@echo -n "Javascript coverage %: "
257259
@grep -B1 Lines coverage/index.html | head -n1 \
@@ -276,6 +278,7 @@ coverage-report-js:
276278
# Runs the C++ tests using the built `cctest` executable.
277279
cctest: all
278280
@out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER)
281+
@out/$(BUILDTYPE)/embedtest "require('./test/embedding/test.js')"
279282

280283
.PHONY: list-gtests
281284
list-gtests:
@@ -529,6 +532,7 @@ test-ci: | clear-stalled build-addons build-js-native-api-tests build-node-api-t
529532
$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
530533
--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
531534
$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
535+
out/Release/embedtest 'require("./test/embedding/test.js")'
532536
@echo "Clean up any leftover processes, error if found."
533537
ps awwx | grep Release/node | grep -v grep | cat
534538
@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
@@ -1258,6 +1262,8 @@ LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
12581262
test/addons/*/*.h \
12591263
test/cctest/*.cc \
12601264
test/cctest/*.h \
1265+
test/embedding/*.cc \
1266+
test/embedding/*.h \
12611267
test/js-native-api/*/*.cc \
12621268
test/js-native-api/*/*.h \
12631269
test/node-api/*/*.cc \

node.gyp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,62 @@
11901190
],
11911191
}, # cctest
11921192

1193+
{
1194+
'target_name': 'embedtest',
1195+
'type': 'executable',
1196+
1197+
'dependencies': [
1198+
'<(node_lib_target_name)',
1199+
'deps/histogram/histogram.gyp:histogram',
1200+
'deps/uvwasi/uvwasi.gyp:uvwasi',
1201+
'node_dtrace_header',
1202+
'node_dtrace_ustack',
1203+
'node_dtrace_provider',
1204+
],
1205+
1206+
'includes': [
1207+
'node.gypi'
1208+
],
1209+
1210+
'include_dirs': [
1211+
'src',
1212+
'tools/msvs/genfiles',
1213+
'deps/v8/include',
1214+
'deps/cares/include',
1215+
'deps/uv/include',
1216+
'deps/uvwasi/include',
1217+
'test/embedding',
1218+
],
1219+
1220+
'sources': [
1221+
'src/node_snapshot_stub.cc',
1222+
'src/node_code_cache_stub.cc',
1223+
'test/embedding/embedtest.cc',
1224+
],
1225+
1226+
'conditions': [
1227+
['OS=="solaris"', {
1228+
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
1229+
}],
1230+
# Skip cctest while building shared lib node for Windows
1231+
[ 'OS=="win" and node_shared=="true"', {
1232+
'type': 'none',
1233+
}],
1234+
[ 'node_shared=="true"', {
1235+
'xcode_settings': {
1236+
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
1237+
},
1238+
}],
1239+
['OS=="win"', {
1240+
'libraries': [
1241+
'Dbghelp.lib',
1242+
'winmm.lib',
1243+
'Ws2_32.lib',
1244+
],
1245+
}],
1246+
],
1247+
}, # embedtest
1248+
11931249
# TODO(joyeecheung): do not depend on node_lib,
11941250
# instead create a smaller static library node_lib_base that does
11951251
# just enough for node_native_module.cc and the cache builder to

src/node_code_cache_stub.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// This file is part of the embedder test, which is intentionally built without
2+
// NODE_WANT_INTERNALS, so we define it here manually.
3+
#define NODE_WANT_INTERNALS 1
4+
15
#include "node_native_module_env.h"
26

37
// This is supposed to be generated by tools/generate_code_cache.js

src/node_snapshot_stub.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// This file is part of the embedder test, which is intentionally built without
2+
// NODE_WANT_INTERNALS, so we define it here manually.
3+
#define NODE_WANT_INTERNALS 1
4+
15
#include "node_main_instance.h"
26

37
namespace node {

test/embedding/embedtest.cc

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#include "node.h"
2+
#include "uv.h"
3+
#include <assert.h>
4+
5+
// Note: This file is being referred to from doc/api/embedding.md, and excerpts
6+
// from it are included in the documentation. Try to keep these in sync.
7+
8+
using node::ArrayBufferAllocator;
9+
using node::Environment;
10+
using node::IsolateData;
11+
using node::MultiIsolatePlatform;
12+
using v8::Context;
13+
using v8::HandleScope;
14+
using v8::Isolate;
15+
using v8::Local;
16+
using v8::Locker;
17+
using v8::MaybeLocal;
18+
using v8::SealHandleScope;
19+
using v8::Value;
20+
using v8::V8;
21+
22+
static int RunNodeInstance(MultiIsolatePlatform* platform,
23+
const std::vector<std::string>& args,
24+
const std::vector<std::string>& exec_args);
25+
26+
int main(int argc, char** argv) {
27+
std::vector<std::string> args(argv, argv + argc);
28+
std::vector<std::string> exec_args;
29+
std::vector<std::string> errors;
30+
int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
31+
for (const std::string& error : errors)
32+
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
33+
if (exit_code != 0) {
34+
return exit_code;
35+
}
36+
37+
std::unique_ptr<MultiIsolatePlatform> platform =
38+
MultiIsolatePlatform::Create(4);
39+
V8::InitializePlatform(platform.get());
40+
V8::Initialize();
41+
42+
int ret = RunNodeInstance(platform.get(), args, exec_args);
43+
44+
V8::Dispose();
45+
V8::ShutdownPlatform();
46+
return ret;
47+
}
48+
49+
int RunNodeInstance(MultiIsolatePlatform* platform,
50+
const std::vector<std::string>& args,
51+
const std::vector<std::string>& exec_args) {
52+
int exit_code = 0;
53+
uv_loop_t loop;
54+
int ret = uv_loop_init(&loop);
55+
if (ret != 0) {
56+
fprintf(stderr, "%s: Failed to initialize loop: %s\n",
57+
args[0].c_str(),
58+
uv_err_name(ret));
59+
return 1;
60+
}
61+
62+
std::shared_ptr<ArrayBufferAllocator> allocator =
63+
ArrayBufferAllocator::Create();
64+
65+
Isolate* isolate = NewIsolate(allocator, &loop, platform);
66+
if (isolate == nullptr) {
67+
fprintf(stderr, "%s: Failed to initialize V8 Isolate\n", args[0].c_str());
68+
return 1;
69+
}
70+
71+
{
72+
Locker locker(isolate);
73+
Isolate::Scope isolate_scope(isolate);
74+
75+
std::unique_ptr<IsolateData, decltype(&node::FreeIsolateData)> isolate_data(
76+
node::CreateIsolateData(isolate, &loop, platform, allocator.get()),
77+
node::FreeIsolateData);
78+
79+
HandleScope handle_scope(isolate);
80+
Local<Context> context = node::NewContext(isolate);
81+
if (context.IsEmpty()) {
82+
fprintf(stderr, "%s: Failed to initialize V8 Context\n", args[0].c_str());
83+
return 1;
84+
}
85+
86+
Context::Scope context_scope(context);
87+
std::unique_ptr<Environment, decltype(&node::FreeEnvironment)> env(
88+
node::CreateEnvironment(isolate_data.get(), context, args, exec_args),
89+
node::FreeEnvironment);
90+
91+
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
92+
env.get(),
93+
"const publicRequire ="
94+
" require('module').createRequire(process.cwd() + '/');"
95+
"globalThis.require = publicRequire;"
96+
"require('vm').runInThisContext(process.argv[1]);");
97+
98+
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
99+
return 1;
100+
101+
{
102+
SealHandleScope seal(isolate);
103+
bool more;
104+
do {
105+
uv_run(&loop, UV_RUN_DEFAULT);
106+
107+
platform->DrainTasks(isolate);
108+
more = uv_loop_alive(&loop);
109+
if (more) continue;
110+
111+
node::EmitBeforeExit(env.get());
112+
more = uv_loop_alive(&loop);
113+
} while (more == true);
114+
}
115+
116+
exit_code = node::EmitExit(env.get());
117+
118+
node::Stop(env.get());
119+
}
120+
121+
bool platform_finished = false;
122+
platform->AddIsolateFinishedCallback(isolate, [](void* data) {
123+
*static_cast<bool*>(data) = true;
124+
}, &platform_finished);
125+
platform->UnregisterIsolate(isolate);
126+
isolate->Dispose();
127+
128+
// Wait until the platform has cleaned up all relevant resources.
129+
while (!platform_finished)
130+
uv_run(&loop, UV_RUN_ONCE);
131+
int err = uv_loop_close(&loop);
132+
assert(err == 0);
133+
134+
return exit_code;
135+
}

test/embedding/test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const child_process = require('child_process');
5+
6+
common.allowGlobals(global.require);
7+
8+
assert.strictEqual(
9+
child_process.spawnSync(process.execPath, ['console.log(42)'])
10+
.stdout.toString().trim(),
11+
'42');
12+
13+
assert.strictEqual(
14+
child_process.spawnSync(process.execPath, ['throw new Error()']).status,
15+
1);
16+
17+
assert.strictEqual(
18+
child_process.spawnSync(process.execPath, ['process.exitCode = 8']).status,
19+
8);

0 commit comments

Comments
 (0)