Skip to content

Commit 4af336d

Browse files
committed
src,test: add full-featured embedder API test
Backport-PR-URL: #35241 PR-URL: #30467 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent b8c9048 commit 4af336d

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:
@@ -531,6 +534,7 @@ test-ci: | clear-stalled bench-addons-build build-addons build-js-native-api-tes
531534
$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
532535
--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
533536
$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
537+
out/Release/embedtest 'require("./test/embedding/test.js")'
534538
@echo "Clean up any leftover processes, error if found."
535539
ps awwx | grep Release/node | grep -v grep | cat
536540
@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
@@ -1274,6 +1278,8 @@ LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
12741278
test/addons/*/*.h \
12751279
test/cctest/*.cc \
12761280
test/cctest/*.h \
1281+
test/embedding/*.cc \
1282+
test/embedding/*.h \
12771283
test/js-native-api/*/*.cc \
12781284
test/js-native-api/*/*.h \
12791285
test/node-api/*/*.cc \

node.gyp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,62 @@
12361236
],
12371237
}, # cctest
12381238

1239+
{
1240+
'target_name': 'embedtest',
1241+
'type': 'executable',
1242+
1243+
'dependencies': [
1244+
'<(node_lib_target_name)',
1245+
'deps/histogram/histogram.gyp:histogram',
1246+
'deps/uvwasi/uvwasi.gyp:uvwasi',
1247+
'node_dtrace_header',
1248+
'node_dtrace_ustack',
1249+
'node_dtrace_provider',
1250+
],
1251+
1252+
'includes': [
1253+
'node.gypi'
1254+
],
1255+
1256+
'include_dirs': [
1257+
'src',
1258+
'tools/msvs/genfiles',
1259+
'deps/v8/include',
1260+
'deps/cares/include',
1261+
'deps/uv/include',
1262+
'deps/uvwasi/include',
1263+
'test/embedding',
1264+
],
1265+
1266+
'sources': [
1267+
'src/node_snapshot_stub.cc',
1268+
'src/node_code_cache_stub.cc',
1269+
'test/embedding/embedtest.cc',
1270+
],
1271+
1272+
'conditions': [
1273+
['OS=="solaris"', {
1274+
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
1275+
}],
1276+
# Skip cctest while building shared lib node for Windows
1277+
[ 'OS=="win" and node_shared=="true"', {
1278+
'type': 'none',
1279+
}],
1280+
[ 'node_shared=="true"', {
1281+
'xcode_settings': {
1282+
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
1283+
},
1284+
}],
1285+
['OS=="win"', {
1286+
'libraries': [
1287+
'Dbghelp.lib',
1288+
'winmm.lib',
1289+
'Ws2_32.lib',
1290+
],
1291+
}],
1292+
],
1293+
}, # embedtest
1294+
12391295
# TODO(joyeecheung): do not depend on node_lib,
12401296
# instead create a smaller static library node_lib_base that does
12411297
# 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
// The stub here is used when configure is run without `--code-cache-path`.

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.get(), &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)