Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: fix argument computation in embedtest #49506

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,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-embedding.js')"
$(NODE) ./test/embedding/test-embedding.js

.PHONY: list-gtests
list-gtests:
Expand Down Expand Up @@ -550,7 +550,7 @@ test-ci: | clear-stalled bench-addons-build build-addons build-js-native-api-tes
$(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-embedding.js")'
$(NODE) ./test/embedding/test-embedding.js
$(info 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}'`; \
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/main/mksnapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ function requireForUserSnapshot(id) {


function main() {
prepareMainThreadExecution(true, false);
prepareMainThreadExecution(false, false);
initializeCallbacks();

let stackTraceLimitDesc;
Expand Down
32 changes: 20 additions & 12 deletions test/embedding/embedtest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
snapshot_as_file = true;
} else if (arg == "--embedder-snapshot-blob") {
assert(i + 1 < args.size());
snapshot_blob_path = args[i + i];
snapshot_blob_path = args[i + 1];
i++;
} else {
filtered_args.push_back(arg);
Expand Down Expand Up @@ -121,9 +121,10 @@ int RunNodeInstance(MultiIsolatePlatform* platform,

if (is_building_snapshot) {
// It contains at least the binary path, the code to snapshot,
// and --embedder-snapshot-create. Insert an anonymous filename
// as process.argv[1].
assert(filtered_args.size() >= 3);
// and --embedder-snapshot-create (which is filtered, so at least
// 2 arguments should remain after filtering).
assert(filtered_args.size() >= 2);
// Insert an anonymous filename as process.argv[1].
filtered_args.insert(filtered_args.begin() + 1,
node::GetAnonymousMainPath());
}
Expand Down Expand Up @@ -153,19 +154,26 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
Context::Scope context_scope(setup->context());

MaybeLocal<Value> loadenv_ret;
if (snapshot) {
if (snapshot) { // Deserializing snapshot
loadenv_ret = node::LoadEnvironment(env, node::StartExecutionCallback{});
} else {
} else if (is_building_snapshot) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we sync the code in the embedding docs (

node/doc/api/embedding.md

Lines 112 to 166 in 6a489df

int RunNodeInstance(MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
int exit_code = 0;
// Setup up a libuv event loop, v8::Isolate, and Node.js Environment.
std::vector<std::string> errors;
std::unique_ptr<CommonEnvironmentSetup> setup =
CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
if (!setup) {
for (const std::string& err : errors)
fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
return 1;
}
Isolate* isolate = setup->isolate();
Environment* env = setup->env();
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
// The v8::Context needs to be entered when node::CreateEnvironment() and
// node::LoadEnvironment() are being called.
Context::Scope context_scope(setup->context());
// Set up the Node.js instance for execution, and run code inside of it.
// There is also a variant that takes a callback and provides it with
// the `require` and `process` objects, so that it can manually compile
// and run scripts as needed.
// The `require` function inside this script does *not* access the file
// system, and can only load built-in Node.js modules.
// `module.createRequire()` is being used to create one that is able to
// load files from the disk, and uses the standard CommonJS file loader
// instead of the internal-only `require` function.
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
env,
"const publicRequire ="
" require('node:module').createRequire(process.cwd() + '/');"
"globalThis.require = publicRequire;"
"require('node:vm').runInThisContext(process.argv[1]);");
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
return 1;
exit_code = node::SpinEventLoop(env).FromMaybe(1);
// node::Stop() can be used to explicitly stop the event loop and keep
// further JavaScript from running. It can be called from any thread,
// and will act like worker.terminate() if called from another thread.
node::Stop(env);
}
return exit_code;
}
) too?

Copy link
Member Author

@joyeecheung joyeecheung Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, the code in there would continue to work, the snapshot capability and the snapshot testing code was added by #45888, so probably out of scope of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc has a reference to this file in

The full code can be found [in the Node.js source tree][embedtest.cc].
, so I thought these were supposed to be in sync but this too lgtm if that's not needed

// Environment created for snapshotting must set process.argv[1] to
// the name of the main script, which was inserted above.
loadenv_ret = node::LoadEnvironment(
env,
// Snapshots do not support userland require()s (yet)
"if (!require('v8').startupSnapshot.isBuildingSnapshot()) {"
" const publicRequire ="
" require('module').createRequire(process.cwd() + '/');"
" globalThis.require = publicRequire;"
"} else globalThis.require = require;"
"const assert = require('assert');"
"assert(require('v8').startupSnapshot.isBuildingSnapshot());"
"globalThis.embedVars = { nön_ascıı: '🏳️‍🌈' };"
"globalThis.require = require;"
"require('vm').runInThisContext(process.argv[2]);");
} else {
loadenv_ret = node::LoadEnvironment(
env,
"const publicRequire = require('module').createRequire(process.cwd() "
"+ '/');"
"globalThis.require = publicRequire;"
"globalThis.embedVars = { nön_ascıı: '🏳️‍🌈' };"
"require('vm').runInThisContext(process.argv[1]);");
}

if (loadenv_ret.IsEmpty()) // There has been a JS exception.
Expand Down
150 changes: 96 additions & 54 deletions test/embedding/test-embedding.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const common = require('../common');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const child_process = require('child_process');
const {
spawnSyncAndExit,
spawnSyncAndExitWithoutError,
} = require('../common/child_process');
const path = require('path');
const fs = require('fs');

Expand All @@ -21,39 +24,54 @@ function resolveBuiltBinary(bin) {

const binary = resolveBuiltBinary('embedtest');

assert.strictEqual(
child_process.spawnSync(binary, ['console.log(42)'])
.stdout.toString().trim(),
'42');

assert.strictEqual(
child_process.spawnSync(binary, ['console.log(embedVars.nön_ascıı)'])
.stdout.toString().trim(),
'🏳️‍🌈');

assert.strictEqual(
child_process.spawnSync(binary, ['console.log(42)'])
.stdout.toString().trim(),
'42');
spawnSyncAndExitWithoutError(
binary,
['console.log(42)'],
{
trim: true,
stdout: '42',
});

assert.strictEqual(
child_process.spawnSync(binary, ['throw new Error()']).status,
1);
spawnSyncAndExitWithoutError(
binary,
['console.log(embedVars.nön_ascıı)'],
{
trim: true,
stdout: '🏳️‍🌈',
});

// Cannot require internals anymore:
assert.strictEqual(
child_process.spawnSync(binary, ['require("lib/internal/test/binding")']).status,
1);
spawnSyncAndExit(
binary,
['throw new Error()'],
{
status: 1,
signal: null,
});

assert.strictEqual(
child_process.spawnSync(binary, ['process.exitCode = 8']).status,
8);
spawnSyncAndExit(
binary,
['require("lib/internal/test/binding")'],
{
status: 1,
signal: null,
});

spawnSyncAndExit(
binary,
['process.exitCode = 8'],
{
status: 8,
signal: null,
});

const fixturePath = JSON.stringify(fixtures.path('exit.js'));
assert.strictEqual(
child_process.spawnSync(binary, [`require(${fixturePath})`, 92]).status,
92);
spawnSyncAndExit(
binary,
[`require(${fixturePath})`, 92],
{
status: 92,
signal: null,
});

function getReadFileCodeForPath(path) {
return `(require("fs").readFileSync(${JSON.stringify(path)}, "utf8"))`;
Expand All @@ -64,31 +82,49 @@ for (const extraSnapshotArgs of [[], ['--embedder-snapshot-as-file']]) {
// readSync + eval since snapshots don't support userland require() (yet)
const snapshotFixture = fixtures.path('snapshot', 'echo-args.js');
const blobPath = tmpdir.resolve('embedder-snapshot.blob');
const buildSnapshotArgs = [
const buildSnapshotExecArgs = [
`eval(${getReadFileCodeForPath(snapshotFixture)})`, 'arg1', 'arg2',
];
const embedTestBuildArgs = [
'--embedder-snapshot-blob', blobPath, '--embedder-snapshot-create',
...extraSnapshotArgs,
];
const runEmbeddedArgs = [
'--embedder-snapshot-blob', blobPath, ...extraSnapshotArgs, 'arg3', 'arg4',
const buildSnapshotArgs = [
...buildSnapshotExecArgs,
...embedTestBuildArgs,
];

const runSnapshotExecArgs = [
'arg3', 'arg4',
];
const embedTestRunArgs = [
'--embedder-snapshot-blob', blobPath,
...extraSnapshotArgs,
];
const runSnapshotArgs = [
...runSnapshotExecArgs,
...embedTestRunArgs,
];

fs.rmSync(blobPath, { force: true });
const child = child_process.spawnSync(binary, [
'--', ...buildSnapshotArgs,
], {
cwd: tmpdir.path,
});
if (child.status !== 0) {
console.log(child.stderr.toString());
console.log(child.stdout.toString());
}
assert.strictEqual(child.status, 0);
const spawnResult = child_process.spawnSync(binary, ['--', ...runEmbeddedArgs]);
assert.deepStrictEqual(JSON.parse(spawnResult.stdout), {
originalArgv: [binary, ...buildSnapshotArgs],
currentArgv: [binary, ...runEmbeddedArgs],
});
spawnSyncAndExitWithoutError(
binary,
[ '--', ...buildSnapshotArgs ],
{ cwd: tmpdir.path },
{});
spawnSyncAndExitWithoutError(
binary,
[ '--', ...runSnapshotArgs ],
{ cwd: tmpdir.path },
{
stdout(output) {
assert.deepStrictEqual(JSON.parse(output), {
originalArgv: [binary, '__node_anonymous_main', ...buildSnapshotExecArgs],
currentArgv: [binary, ...runSnapshotExecArgs],
});
return true;
},
});
}

// Create workers and vm contexts after deserialization
Expand All @@ -99,14 +135,20 @@ for (const extraSnapshotArgs of [[], ['--embedder-snapshot-as-file']]) {
`eval(${getReadFileCodeForPath(snapshotFixture)})`,
'--embedder-snapshot-blob', blobPath, '--embedder-snapshot-create',
];
const runEmbeddedArgs = [
'--embedder-snapshot-blob', blobPath,
];

fs.rmSync(blobPath, { force: true });
assert.strictEqual(child_process.spawnSync(binary, [
'--', ...buildSnapshotArgs,
], {
cwd: tmpdir.path,
}).status, 0);
assert.strictEqual(
child_process.spawnSync(binary, ['--', '--embedder-snapshot-blob', blobPath]).status,
0);

spawnSyncAndExitWithoutError(
binary,
[ '--', ...buildSnapshotArgs ],
{ cwd: tmpdir.path },
{});
spawnSyncAndExitWithoutError(
binary,
[ '--', ...runEmbeddedArgs ],
{ cwd: tmpdir.path },
{});
}