Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,53 @@
prefix sequential
# To mark a test as flaky, list the test name in the appropriate section
# below, without ".js", followed by ": PASS,FLAKY". Example:
# sample-test : PASS,FLAKY
[true] # This section applies to all platforms
# https://github.com/nodejs/node/issues/27611#issuecomment-613100468
test-cpu-prof-dir-worker: PASS, FLAKY
# https://github.com/nodejs/node/issues/44898
test-watch-mode: PASS, FLAKY
test-watch-mode-inspect: PASS, FLAKY
# https://github.com/nodejs/node/issues/47409
test-http2-large-file: PASS, FLAKY
[$system==win32]
# https://github.com/nodejs/node/issues/49630
test-single-executable-application-snapshot: PASS, FLAKY
test-single-executable-application-snapshot-and-code-cache: PASS, FLAKY
# https://github.com/nodejs/node/issues/47409
test-http2-large-file: PASS, FLAKY
[$system==linux]
# https://github.com/nodejs/node/issues/54817
test-http-server-request-timeouts-mixed: PASS, FLAKY
[$system==macos]
# https://github.com/nodejs/node/issues/54816
test-single-executable-application-empty: PASS, FLAKY
# https://github.com/nodejs/node/issues/43465
test-http-server-request-timeouts-mixed: PASS, FLAKY
[$system==solaris] # Also applies to SmartOS
test-worker-prof: PASS, FLAKY
[$system==freebsd]
[$system==aix]
# https://github.com/nodejs/node/pull/29054
test-buffer-creation-regression: SKIP
[$system==ibmi]
# https://github.com/nodejs/node/pull/29054
test-buffer-creation-regression: SKIP
# https://github.com/nodejs/node/pull/30819
test-perf-hooks: SKIP
# https://github.com/nodejs/node/issues/39683
test-tls-psk-client: PASS, FLAKY
[$arch==arm]
# https://github.com/nodejs/node/issues/49933
test-watch-mode-inspect: SKIP

View File

@ -0,0 +1,328 @@
'use strict';
// Flags: --expose-gc --expose-internals --no-warnings --test-udp-no-try-send
const common = require('../common');
const { internalBinding } = require('internal/test/binding');
const assert = require('assert');
const fs = require('fs');
const v8 = require('v8');
const fsPromises = fs.promises;
const net = require('net');
const providers = { ...internalBinding('async_wrap').Providers };
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { getSystemErrorName } = require('util');
// Make sure that all Providers are tested.
{
const hooks = require('async_hooks').createHook({
init(id, type) {
if (type === 'NONE')
throw new Error('received a provider type of NONE');
delete providers[type];
},
}).enable();
process.on('beforeExit', common.mustCall(() => {
// This garbage collection call verifies that the wraps being garbage
// collected doesn't resurrect the process again due to weirdly timed
// uv_close calls and other similar instruments in destructors.
global.gc();
process.removeAllListeners('uncaughtException');
hooks.disable();
delete providers.NONE; // Should never be used.
// See test/pseudo-tty/test-async-wrap-getasyncid-tty.js
// Requires an 'actual' tty fd to be available.
delete providers.TTYWRAP;
// TODO(jasnell): Test for these
delete providers.HTTP2SESSION;
delete providers.HTTP2STREAM;
delete providers.HTTP2PING;
delete providers.HTTP2SETTINGS;
// TODO(addaleax): Test for these
delete providers.STREAMPIPE;
delete providers.MESSAGEPORT;
delete providers.WORKER;
// TODO(danbev): Test for these
delete providers.JSUDPWRAP;
delete providers.KEYPAIRGENREQUEST;
delete providers.KEYGENREQUEST;
delete providers.KEYEXPORTREQUEST;
delete providers.CIPHERREQUEST;
delete providers.DERIVEBITSREQUEST;
delete providers.SCRYPTREQUEST;
delete providers.SIGNREQUEST;
delete providers.VERIFYREQUEST;
delete providers.HASHREQUEST;
delete providers.HTTPCLIENTREQUEST;
delete providers.HTTPINCOMINGMESSAGE;
delete providers.ELDHISTOGRAM;
delete providers.SIGINTWATCHDOG;
delete providers.WORKERHEAPSNAPSHOT;
delete providers.WORKERHEAPSTATISTICS;
delete providers.BLOBREADER;
delete providers.RANDOMPRIMEREQUEST;
delete providers.CHECKPRIMEREQUEST;
delete providers.QUIC_LOGSTREAM;
delete providers.QUIC_PACKET;
delete providers.QUIC_UDP;
delete providers.QUIC_ENDPOINT;
delete providers.QUIC_SESSION;
delete providers.QUIC_STREAM;
const objKeys = Object.keys(providers);
if (objKeys.length > 0)
process._rawDebug(objKeys);
assert.strictEqual(objKeys.length, 0);
}));
}
function testUninitialized(req, ctor_name) {
assert.strictEqual(typeof req.getAsyncId, 'function');
assert.strictEqual(req.getAsyncId(), -1);
assert.strictEqual(req.constructor.name, ctor_name);
}
function testInitialized(req, ctor_name) {
assert.strictEqual(typeof req.getAsyncId, 'function');
assert(Number.isSafeInteger(req.getAsyncId()));
assert(req.getAsyncId() > 0);
assert.strictEqual(req.constructor.name, ctor_name);
}
{
const cares = internalBinding('cares_wrap');
const dns = require('dns');
testUninitialized(new cares.GetAddrInfoReqWrap(), 'GetAddrInfoReqWrap');
testUninitialized(new cares.GetNameInfoReqWrap(), 'GetNameInfoReqWrap');
testUninitialized(new cares.QueryReqWrap(), 'QueryReqWrap');
testInitialized(dns.lookup('www.google.com', () => {}), 'GetAddrInfoReqWrap');
testInitialized(dns.lookupService('::1', 22, () => {}), 'GetNameInfoReqWrap');
const resolver = new dns.Resolver();
resolver.setServers(['127.0.0.1']);
testInitialized(resolver._handle, 'ChannelWrap');
testInitialized(resolver.resolve6('::1', () => {}), 'QueryReqWrap');
resolver.cancel();
}
{
const FSEvent = internalBinding('fs_event_wrap').FSEvent;
testInitialized(new FSEvent(), 'FSEvent');
}
{
const JSStream = internalBinding('js_stream').JSStream;
testInitialized(new JSStream(), 'JSStream');
}
{
// We don't want to expose getAsyncId for promises but we need to construct
// one so that the corresponding provider type is removed from the
// providers list.
new Promise((res) => res(5));
}
if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
const crypto = require('crypto');
// The handle for PBKDF2 and RandomBytes isn't returned by the function call,
// so need to check it from the callback.
const mc = common.mustCall(function pb() {
testInitialized(this, 'PBKDF2Job');
});
crypto.pbkdf2('password', 'salt', 1, 20, 'sha256', mc);
crypto.randomBytes(1, common.mustCall(function rb() {
testInitialized(this, 'RandomBytesJob');
}));
if (typeof internalBinding('crypto').ScryptJob === 'function') {
crypto.scrypt('password', 'salt', 8, common.mustCall(function() {
testInitialized(this, 'ScryptJob');
}));
}
}
{
const binding = internalBinding('fs');
const path = require('path');
const FSReqCallback = binding.FSReqCallback;
const req = new FSReqCallback();
req.oncomplete = () => { };
testInitialized(req, 'FSReqCallback');
binding.access(path.toNamespacedPath('../'), fs.constants.F_OK, req);
const StatWatcher = binding.StatWatcher;
testInitialized(new StatWatcher(), 'StatWatcher');
}
{
const { HTTPParser } = require('_http_common');
const parser = new HTTPParser();
testUninitialized(parser, 'HTTPParser');
parser.initialize(HTTPParser.REQUEST, {});
testInitialized(parser, 'HTTPParser');
}
{
const Gzip = require('zlib').Gzip;
testInitialized(new Gzip()._handle, 'Zlib');
}
{
const binding = internalBinding('pipe_wrap');
const handle = new binding.Pipe(binding.constants.IPC);
testInitialized(handle, 'Pipe');
}
{
tmpdir.refresh();
const server = net.createServer(common.mustCall((socket) => {
server.close();
})).listen(common.PIPE, common.mustCall(() => {
const binding = internalBinding('pipe_wrap');
const handle = new binding.Pipe(binding.constants.SOCKET);
testInitialized(handle, 'Pipe');
const req = new binding.PipeConnectWrap();
testUninitialized(req, 'PipeConnectWrap');
req.address = common.PIPE;
req.oncomplete = common.mustCall(() => handle.close());
handle.connect(req, req.address, req.oncomplete);
testInitialized(req, 'PipeConnectWrap');
}));
}
{
const Process = internalBinding('process_wrap').Process;
testInitialized(new Process(), 'Process');
}
{
const { Signal } = internalBinding('signal_wrap');
testInitialized(new Signal(), 'Signal');
}
{
async function openTest() {
const fd = await fsPromises.open(__filename, 'r');
testInitialized(fd, 'FileHandle');
await fd.close();
}
openTest().then(common.mustCall());
}
{
const binding = internalBinding('stream_wrap');
testUninitialized(new binding.WriteWrap(), 'WriteWrap');
}
{
const stream_wrap = internalBinding('stream_wrap');
const tcp_wrap = internalBinding('tcp_wrap');
const server = net.createServer(common.mustCall((socket) => {
server.close();
socket.on('data', () => {
socket.end();
socket.destroy();
});
socket.resume();
})).listen(0, common.localhostIPv4, common.mustCall(() => {
const handle = new tcp_wrap.TCP(tcp_wrap.constants.SOCKET);
const req = new tcp_wrap.TCPConnectWrap();
const sreq = new stream_wrap.ShutdownWrap();
testInitialized(handle, 'TCP');
testUninitialized(req, 'TCPConnectWrap');
testUninitialized(sreq, 'ShutdownWrap');
sreq.oncomplete = common.mustCall(() => {
handle.close();
});
req.oncomplete = common.mustCall(writeData);
function writeData() {
const wreq = new stream_wrap.WriteWrap();
wreq.handle = handle;
wreq.oncomplete = () => {
handle.shutdown(sreq);
testInitialized(sreq, 'ShutdownWrap');
};
const err = handle.writeLatin1String(wreq, 'hi'.repeat(100000));
if (err)
throw new Error(`write failed: ${getSystemErrorName(err)}`);
if (!stream_wrap.streamBaseState[stream_wrap.kLastWriteWasAsync]) {
testUninitialized(wreq, 'WriteWrap');
// Synchronous finish. Write more data until we hit an
// asynchronous write.
return writeData();
}
testInitialized(wreq, 'WriteWrap');
}
req.address = common.localhostIPv4;
req.port = server.address().port;
const err = handle.connect(req, req.address, req.port);
assert.strictEqual(err, 0);
testInitialized(req, 'TCPConnectWrap');
}));
}
if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap');
const tcp = new TCP(TCPConstants.SOCKET);
const ca = fixtures.readKey('rsa_ca.crt');
const cert = fixtures.readKey('rsa_cert.crt');
const key = fixtures.readKey('rsa_private.pem');
const credentials = require('tls').createSecureContext({ ca, cert, key });
// TLSWrap is exposed, but needs to be instantiated via tls_wrap.wrap().
const tls_wrap = internalBinding('tls_wrap');
testInitialized(tls_wrap.wrap(tcp, credentials.context, true, false), 'TLSWrap');
}
{
const binding = internalBinding('udp_wrap');
const handle = new binding.UDP();
const req = new binding.SendWrap();
testInitialized(handle, 'UDP');
testUninitialized(req, 'SendWrap');
handle.bind('0.0.0.0', 0, undefined);
const addr = {};
handle.getsockname(addr);
req.address = '127.0.0.1';
req.port = addr.port;
req.oncomplete = () => handle.close();
handle.send(req, [Buffer.alloc(1)], 1, req.port, req.address, true);
testInitialized(req, 'SendWrap');
}
// PROVIDER_HEAPDUMP
{
v8.getHeapSnapshot().destroy();
}
// DIRHANDLE
{
const dirBinding = internalBinding('fs_dir');
const handle = dirBinding.opendir('./', 'utf8', undefined, {});
testInitialized(handle, 'DirHandle');
}

View File

@ -0,0 +1,36 @@
'use strict';
const common = require('../common');
const assert = require('assert');
function test(arrayBuffer, offset, length) {
const uint8Array = new Uint8Array(arrayBuffer, offset, length);
for (let i = 0; i < length; i += 1) {
uint8Array[i] = 1;
}
const buffer = Buffer.from(arrayBuffer, offset, length);
for (let i = 0; i < length; i += 1) {
assert.strictEqual(buffer[i], 1);
}
}
const acceptableOOMErrors = [
'Array buffer allocation failed',
'Invalid array buffer length',
];
const length = 1000;
const offset = 4294967296; /* 1 << 32 */
const size = offset + length;
let arrayBuffer;
try {
arrayBuffer = new ArrayBuffer(size);
} catch (e) {
if (e instanceof RangeError && acceptableOOMErrors.includes(e.message))
common.skip(`Unable to allocate ${size} bytes for ArrayBuffer`);
throw e;
}
test(arrayBuffer, offset, length);

View File

@ -0,0 +1,78 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
if (common.isWindows)
common.skip('no RLIMIT_NOFILE on Windows');
const assert = require('assert');
const child_process = require('child_process');
const fs = require('fs');
const ulimit = Number(child_process.execSync('ulimit -Hn'));
if (ulimit > 64 || Number.isNaN(ulimit)) {
const [cmd, opts] = common.escapePOSIXShell`ulimit -n 64 && "${process.execPath}" "${__filename}"`;
// Sorry about this nonsense. It can be replaced if
// https://github.com/nodejs/node-v0.x-archive/pull/2143#issuecomment-2847886
// ever happens.
const result = child_process.spawnSync(
'/bin/sh',
['-c', cmd],
opts,
);
assert.strictEqual(result.stdout.toString(), '');
assert.strictEqual(result.stderr.toString(), '');
assert.strictEqual(result.status, 0);
assert.strictEqual(result.error, undefined);
return;
}
const openFds = [];
for (;;) {
try {
openFds.push(fs.openSync(__filename, 'r'));
} catch (err) {
assert.strictEqual(err.code, 'EMFILE');
break;
}
}
// Should emit an error, not throw.
const proc = child_process.spawn(process.execPath, ['-e', '0']);
// Verify that stdio is not setup on EMFILE or ENFILE.
assert.strictEqual(proc.stdin, undefined);
assert.strictEqual(proc.stdout, undefined);
assert.strictEqual(proc.stderr, undefined);
assert.strictEqual(proc.stdio, undefined);
proc.on('error', common.mustCall(function(err) {
assert.strictEqual(err.code, 'EMFILE');
}));
proc.on('exit', common.mustNotCall('"exit" event should not be emitted'));
// Close one fd for LSan
if (openFds.length >= 1) {
fs.closeSync(openFds.pop());
}

View File

@ -0,0 +1,160 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const assert = require('assert');
const { execFileSync, execSync, spawnSync } = require('child_process');
const { getSystemErrorName } = require('util');
const TIMER = 200;
let SLEEP = 2000;
if (common.isWindows) {
// Some of the windows machines in the CI need more time to launch
// and receive output from child processes.
// https://github.com/nodejs/build/issues/3014
SLEEP = 10000;
}
// Verify that stderr is not accessed when a bad shell is used
assert.throws(
function() { execSync('exit -1', { shell: 'bad_shell' }); },
/spawnSync bad_shell ENOENT/
);
assert.throws(
function() { execFileSync('exit -1', { shell: 'bad_shell' }); },
/spawnSync bad_shell ENOENT/
);
let caught = false;
let ret, err;
const start = Date.now();
try {
const cmd = `"${common.isWindows ? process.execPath : '$NODE'}" -e "setTimeout(function(){}, ${SLEEP});"`;
ret = execSync(cmd, { env: { ...process.env, NODE: process.execPath }, timeout: TIMER });
} catch (e) {
caught = true;
assert.strictEqual(getSystemErrorName(e.errno), 'ETIMEDOUT');
err = e;
} finally {
assert.strictEqual(ret, undefined,
`should not have a return value, received ${ret}`);
assert.ok(caught, 'execSync should throw');
const end = Date.now() - start;
assert(end < SLEEP);
assert(err.status > 128 || err.signal, `status: ${err.status}, signal: ${err.signal}`);
}
assert.throws(function() {
execSync('iamabadcommand');
}, /Command failed: iamabadcommand/);
const msg = 'foobar';
const msgBuf = Buffer.from(`${msg}\n`);
// console.log ends every line with just '\n', even on Windows.
const cmd = `"${common.isWindows ? process.execPath : '$NODE'}" -e "console.log('${msg}');"`;
const env = common.isWindows ? process.env : { ...process.env, NODE: process.execPath };
{
const ret = execSync(cmd, common.isWindows ? undefined : { env });
assert.strictEqual(ret.length, msgBuf.length);
assert.deepStrictEqual(ret, msgBuf);
}
{
const ret = execSync(cmd, { encoding: 'utf8', env });
assert.strictEqual(ret, `${msg}\n`);
}
const args = [
'-e',
`console.log("${msg}");`,
];
{
const ret = execFileSync(process.execPath, args);
assert.deepStrictEqual(ret, msgBuf);
}
{
const ret = execFileSync(process.execPath, args, { encoding: 'utf8' });
assert.strictEqual(ret, `${msg}\n`);
}
// Verify that the cwd option works.
// See https://github.com/nodejs/node-v0.x-archive/issues/7824.
{
const cwd = tmpdir.path;
const cmd = common.isWindows ? 'echo %cd%' : 'pwd';
const response = execSync(cmd, { cwd });
assert.strictEqual(response.toString().trim(), cwd);
}
// Verify that stderr is not accessed when stdio = 'ignore'.
// See https://github.com/nodejs/node-v0.x-archive/issues/7966.
{
assert.throws(function() {
execSync('exit -1', { stdio: 'ignore' });
}, /Command failed: exit -1/);
}
// Verify the execFileSync() behavior when the child exits with a non-zero code.
{
const args = ['-e', 'process.exit(1)'];
const spawnSyncResult = spawnSync(process.execPath, args);
const spawnSyncKeys = Object.keys(spawnSyncResult).sort();
assert.deepStrictEqual(spawnSyncKeys, [
'output',
'pid',
'signal',
'status',
'stderr',
'stdout',
]);
assert.throws(() => {
execFileSync(process.execPath, args);
}, (err) => {
const msg = `Command failed: ${process.execPath} ${args.join(' ')}`;
assert(err instanceof Error);
assert.strictEqual(err.message, msg);
assert.strictEqual(err.status, 1);
assert.strictEqual(typeof err.pid, 'number');
spawnSyncKeys
.filter((key) => key !== 'pid')
.forEach((key) => {
assert.deepStrictEqual(err[key], spawnSyncResult[key]);
});
return true;
});
}
// Verify the shell option works properly
execFileSync(`"${common.isWindows ? process.execPath : '$NODE'}"`, [], {
encoding: 'utf8', shell: true, env
});

View File

@ -0,0 +1,59 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
// Open a chain of five Node processes each a child of the next. The final
// process exits immediately. Each process in the chain is instructed to exit
// when its child exits.
// https://github.com/joyent/node/issues/1726
const assert = require('assert');
const ch = require('child_process');
const gen = +(process.argv[2] || 0);
const maxGen = 5;
if (gen === maxGen) {
console.error('hit maxGen, exiting', maxGen);
return;
}
const child = ch.spawn(process.execPath, [__filename, gen + 1], {
stdio: [ 'ignore', 'pipe', 'ignore' ]
});
assert.ok(!child.stdin);
assert.ok(child.stdout);
assert.ok(!child.stderr);
console.error('gen=%d, pid=%d', gen, process.pid);
child.on('exit', function(code) {
console.error('exit %d from gen %d', code, gen + 1);
});
child.stdout.pipe(process.stdout);
child.stdout.on('close', function() {
console.error('child.stdout close gen=%d', gen);
});

View File

@ -0,0 +1,84 @@
'use strict';
const common = require('../common');
// On some OS X versions, when passing fd's between processes:
// When the handle associated to a specific file descriptor is closed by the
// sender process before it's received in the destination, the handle is indeed
// closed while it should remain opened. In order to fix this behavior, don't
// close the handle until the `NODE_HANDLE_ACK` is received by the sender.
// This test is basically `test-cluster-net-send` but creating lots of workers
// so the issue reproduces on OS X consistently.
if (common.isPi()) {
common.skip('Too slow for Raspberry Pi devices');
}
const assert = require('assert');
const { fork } = require('child_process');
const net = require('net');
const N = 80;
let messageCallbackCount = 0;
function forkWorker() {
const messageCallback = (msg, handle) => {
messageCallbackCount++;
assert.strictEqual(msg, 'handle');
assert.ok(handle);
worker.send('got');
let recvData = '';
handle.on('data', common.mustCall((data) => {
recvData += data;
}));
handle.on('end', () => {
assert.strictEqual(recvData, 'hello');
worker.kill();
});
};
const worker = fork(__filename, ['child']);
worker.on('error', (err) => {
if (/\bEAGAIN\b/.test(err.message)) {
forkWorker();
return;
}
throw err;
});
worker.once('message', messageCallback);
}
if (process.argv[2] !== 'child') {
for (let i = 0; i < N; ++i) {
forkWorker();
}
process.on('exit', () => { assert.strictEqual(messageCallbackCount, N); });
} else {
let socket;
let cbcalls = 0;
function socketConnected() {
if (++cbcalls === 2)
process.send('handle', socket);
}
// As a side-effect, listening for the message event will ref the IPC channel,
// so the child process will stay alive as long as it has a parent process/IPC
// channel. Once this is done, we can unref our client and server sockets, and
// the only thing keeping this worker alive will be IPC. This is important,
// because it means a worker with no parent will have no referenced handles,
// thus no work to do, and will exit immediately, preventing process leaks.
process.on('message', common.mustCall());
const server = net.createServer((c) => {
process.once('message', (msg) => {
assert.strictEqual(msg, 'got');
c.end('hello');
});
socketConnected();
}).unref();
server.listen(0, common.localhostIPv4, () => {
const { port } = server.address();
socket = net.connect(port, common.localhostIPv4, socketConnected).unref();
});
}

View File

@ -0,0 +1,56 @@
'use strict';
const common = require('../common');
const { exec } = require('child_process');
const { test } = require('node:test');
const fixtures = require('../common/fixtures');
// Test both sets of arguments that check syntax
const syntaxArgs = [
'-c',
'--check',
];
// Match on the name of the `Error` but not the message as it is different
// depending on the JavaScript engine.
const syntaxErrorRE = /^SyntaxError: \b/m;
// Test bad syntax with and without shebang
[
'syntax/bad_syntax.js',
'syntax/bad_syntax',
'syntax/bad_syntax_shebang.js',
'syntax/bad_syntax_shebang',
].forEach((file) => {
const path = fixtures.path(file);
// Loop each possible option, `-c` or `--check`
syntaxArgs.forEach((flag) => {
test(`Checking syntax for ${file} with ${flag}`, async (t) => {
try {
const { stdout, stderr } = await execNode(flag, path);
// No stdout should be produced
t.assert.strictEqual(stdout, '');
// Stderr should have a syntax error message
t.assert.match(stderr, syntaxErrorRE);
// stderr should include the filename
t.assert.ok(stderr.startsWith(path));
} catch (err) {
t.assert.strictEqual(err.code, 1);
}
});
});
});
// Helper function to promisify exec
function execNode(flag, path) {
const { promise, resolve, reject } = Promise.withResolvers();
exec(...common.escapePOSIXShell`"${process.execPath}" ${flag} "${path}"`, (err, stdout, stderr) => {
if (err) return reject({ ...err, stdout, stderr });
resolve({ stdout, stderr });
});
return promise;
}

View File

@ -0,0 +1,36 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { exec } = require('child_process');
const fixtures = require('../common/fixtures');
// Test both sets of arguments that check syntax
const syntaxArgs = [
'-c',
'--check',
];
const notFoundRE = /^Error: Cannot find module/m;
// test file not found
[
'syntax/file_not_found.js',
'syntax/file_not_found',
].forEach(function(file) {
file = fixtures.path(file);
// Loop each possible option, `-c` or `--check`
syntaxArgs.forEach(function(flag) {
exec(...common.escapePOSIXShell`"${process.execPath}" ${flag} "${file}"`, common.mustCall((err, stdout, stderr) => {
// No stdout should be produced
assert.strictEqual(stdout, '');
// `stderr` should have a module not found error message.
assert.match(stderr, notFoundRE);
assert.strictEqual(err.code, 1,
`code ${err.code} !== 1 for error:\n\n${err}`);
}));
});
});

View File

@ -0,0 +1,39 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { exec } = require('child_process');
const fixtures = require('../common/fixtures');
// Test both sets of arguments that check syntax
const syntaxArgs = [
'-c',
'--check',
];
// Test good syntax with and without shebang
[
'syntax/good_syntax.js',
'syntax/good_syntax',
'syntax/good_syntax.mjs',
'syntax/good_syntax_shebang.js',
'syntax/good_syntax_shebang',
'syntax/illegal_if_not_wrapped.js',
].forEach(function(file) {
file = fixtures.path(file);
// Loop each possible option, `-c` or `--check`
syntaxArgs.forEach(function(flag) {
exec(...common.escapePOSIXShell`"${process.execPath}" ${flag} "${file}"`, common.mustCall((err, stdout, stderr) => {
if (err) {
console.log('-- stdout --');
console.log(stdout);
console.log('-- stderr --');
console.log(stderr);
}
assert.ifError(err);
assert.strictEqual(stdout, '');
assert.strictEqual(stderr, '');
}));
});
});

View File

@ -0,0 +1,34 @@
'use strict';
require('../common');
const assert = require('assert');
const { spawnSyncAndExit } = require('../common/child_process');
const fixtures = require('../common/fixtures');
const node = process.execPath;
// Match on the name of the `Error` but not the message as it is different
// depending on the JavaScript engine.
const syntaxErrorRE = /^SyntaxError: \b/m;
// Should work with -r flags
['-c', '--check'].forEach(function(checkFlag) {
['-r', '--require'].forEach(function(requireFlag) {
const preloadFile = fixtures.path('no-wrapper.js');
const file = fixtures.path('syntax', 'illegal_if_not_wrapped.js');
const args = [requireFlag, preloadFile, checkFlag, file];
spawnSyncAndExit(node, args, {
status: 1,
signal: null,
trim: true,
stdout: '',
stderr(output) {
// stderr should have a syntax error message
assert.match(output, syntaxErrorRE);
// stderr should include the filename
assert(output.startsWith(file), `${output} starts with ${file}`);
}
});
});
});

View File

@ -0,0 +1,37 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
// A test to ensure that cluster properly interoperates with the
// --inspect-brk option.
const assert = require('assert');
const cluster = require('cluster');
const debuggerPort = common.PORT;
if (cluster.isPrimary) {
function test(execArgv) {
cluster.setupPrimary({
execArgv: execArgv,
stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'pipe']
});
const worker = cluster.fork();
// Debugger listening on port [port].
worker.process.stderr.once('data', common.mustCall(function() {
worker.process.kill('SIGTERM');
}));
worker.process.on('exit', common.mustCall(function(code, signal) {
assert.strictEqual(signal, 'SIGTERM');
}));
}
test(['--inspect-brk']);
test([`--inspect-brk=${debuggerPort}`]);
} else {
// Cluster worker is at a breakpoint, should not reach here.
assert.fail('Test failed: cluster worker should be at a breakpoint.');
}

View File

@ -0,0 +1,58 @@
'use strict';
const common = require('../common');
if (!common.hasIPv6)
common.skip('no IPv6 support');
const assert = require('assert');
const cluster = require('cluster');
const net = require('net');
// This test ensures that the `ipv6Only` option in `net.Server.listen()`
// works as expected when we use cluster with `SCHED_NONE` schedulingPolicy.
cluster.schedulingPolicy = cluster.SCHED_NONE;
const host = '::';
const WORKER_ACCOUNT = 3;
if (cluster.isPrimary) {
const workers = [];
for (let i = 0; i < WORKER_ACCOUNT; i += 1) {
const myWorker = new Promise((resolve) => {
const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
assert.strictEqual(statusCode, 0);
})).on('listening', common.mustCall((workerAddress) => {
assert.strictEqual(workerAddress.addressType, 6);
assert.strictEqual(workerAddress.address, host);
assert.strictEqual(workerAddress.port, common.PORT);
resolve(worker);
}));
});
workers.push(myWorker);
}
Promise.all(workers).then(common.mustCall((resolvedWorkers) => {
// Make sure the `ipv6Only` option works. This is the part of the test that
// requires the whole test to use `common.PORT` rather than port `0`. If it
// used port `0` instead, then the operating system can supply a port that
// is available for the IPv6 interface but in use by the IPv4 interface.
// Refs: https://github.com/nodejs/node/issues/29679
const server = net.createServer().listen({
host: '0.0.0.0',
port: common.PORT,
}, common.mustCall(() => {
// Exit.
server.close();
resolvedWorkers.forEach((resolvedWorker) => {
resolvedWorker.disconnect();
});
}));
}));
} else {
net.createServer().listen({
host,
port: common.PORT,
ipv6Only: true,
}, common.mustCall());
}

View File

@ -0,0 +1,63 @@
'use strict';
const common = require('../common');
if (!common.hasIPv6)
common.skip('no IPv6 support');
const assert = require('assert');
const cluster = require('cluster');
const net = require('net');
// This test ensures that the `ipv6Only` option in `net.Server.listen()`
// works as expected when we use cluster with `SCHED_RR` schedulingPolicy.
cluster.schedulingPolicy = cluster.SCHED_RR;
const host = '::';
const WORKER_ACCOUNT = 3;
if (cluster.isPrimary) {
const workers = [];
let address;
for (let i = 0; i < WORKER_ACCOUNT; i += 1) {
const myWorker = new Promise((resolve) => {
const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
assert.strictEqual(statusCode, 0);
})).on('listening', common.mustCall((workerAddress) => {
if (!address) {
address = workerAddress;
} else {
assert.deepStrictEqual(workerAddress, address);
}
resolve(worker);
}));
});
workers.push(myWorker);
}
Promise.all(workers).then(common.mustCall((resolvedWorkers) => {
// Make sure the `ipv6Only` option works. Should be able to use the port on
// IPv4.
const server = net.createServer().listen({
host: '0.0.0.0',
port: address.port,
}, common.mustCall(() => {
// Exit.
server.close();
resolvedWorkers.forEach((resolvedWorker) => {
resolvedWorker.disconnect();
});
}));
}));
} else {
// As the cluster member has the potential to grab any port
// from the environment, this can cause collision when primary
// obtains the port from cluster member and tries to listen on.
// So move this to sequential, and provide a static port.
// Refs: https://github.com/nodejs/node/issues/25813
net.createServer().listen({
host: host,
port: common.PORT,
ipv6Only: true,
}, common.mustCall());
}

View File

@ -0,0 +1,54 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const cluster = require('cluster');
const net = require('net');
const payload = 'a'.repeat(800004);
if (cluster.isPrimary) {
const server = net.createServer();
server.on('connection', common.mustCall((socket) => { socket.unref(); }));
const worker = cluster.fork();
worker.on('message', common.mustCall(({ payload: received }, handle) => {
assert.strictEqual(payload, received);
assert(handle instanceof net.Socket);
server.close();
handle.destroy();
}));
server.listen(0, common.mustCall(() => {
const port = server.address().port;
const socket = new net.Socket();
socket.connect(port, (err) => {
assert.ifError(err);
worker.send({ payload }, socket);
});
}));
} else {
process.on('message', common.mustCall(({ payload: received }, handle) => {
assert.strictEqual(payload, received);
assert(handle instanceof net.Socket);
// On macOS, the primary process might not receive a message if it is sent
// to soon, and then subsequent messages are also sometimes not received.
//
// (Is this a bug or expected operating system behavior like the way a file
// watcher is returned before it's actually watching the file system on
// macOS?)
//
// Send a second message after a delay on macOS.
//
// Refs: https://github.com/nodejs/node/issues/14747
if (common.isMacOS)
setTimeout(() => { process.send({ payload }, handle); }, 1000);
else
process.send({ payload }, handle);
// Prepare for a clean exit.
process.channel.unref();
}));
}

View File

@ -0,0 +1,36 @@
'use strict';
// Test --cpu-prof without --cpu-prof-interval. Here we just verify that
// we manage to generate a profile since it's hard to tell whether we
// can sample our target function with the default sampling rate across
// different platforms and machine configurations.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
env
} = require('../common/cpu-prof');
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof',
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
const profiles = getCpuProfiles(tmpdir.path);
assert.strictEqual(profiles.length, 1);
}

View File

@ -0,0 +1,44 @@
'use strict';
// This tests that relative --cpu-prof-dir works.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const fs = require('fs');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
// relative --cpu-prof-dir
{
tmpdir.refresh();
const dir = tmpdir.resolve('prof');
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
'--cpu-prof-dir',
dir,
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
assert(fs.existsSync(dir));
const profiles = getCpuProfiles(dir);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci.js');
}

View File

@ -0,0 +1,47 @@
'use strict';
// This tests that --cpu-prof-dir and --cpu-prof-name works together.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
{
tmpdir.refresh();
const dir = tmpdir.resolve('prof');
const file = path.join(dir, 'test.cpuprofile');
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
'--cpu-prof-name',
'test.cpuprofile',
'--cpu-prof-dir',
dir,
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
assert(fs.existsSync(dir));
const profiles = getCpuProfiles(dir);
assert.deepStrictEqual(profiles, [file]);
verifyFrames(output, file, 'fibonacci.js');
}

View File

@ -0,0 +1,44 @@
'use strict';
// This tests that relative --cpu-prof-dir works.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const fs = require('fs');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
// relative --cpu-prof-dir
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
'--cpu-prof-dir',
'prof',
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
const dir = tmpdir.resolve('prof');
assert(fs.existsSync(dir));
const profiles = getCpuProfiles(dir);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci.js');
}

View File

@ -0,0 +1,54 @@
'use strict';
// This tests that --cpu-prof-dir works for workers.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const fs = require('fs');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
getFrames
} = require('../common/cpu-prof');
// --cpu-prof-dir with worker
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof-interval',
kCpuProfInterval,
'--cpu-prof-dir',
'prof',
'--cpu-prof',
fixtures.path('workload', 'fibonacci-worker.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
const dir = tmpdir.resolve('prof');
assert(fs.existsSync(dir));
const profiles = getCpuProfiles(dir);
assert.strictEqual(profiles.length, 2);
const profile1 = getFrames(profiles[0], 'fibonacci.js');
const profile2 = getFrames(profiles[1], 'fibonacci.js');
if (profile1.frames.length === 0 && profile2.frames.length === 0) {
// Show native debug output and the profile for debugging.
console.log(output.stderr.toString());
console.log('CPU path: ', profiles[0]);
console.log(profile1.nodes);
console.log('CPU path: ', profiles[1]);
console.log(profile2.nodes);
}
assert(profile1.frames.length > 0 || profile2.frames.length > 0);
}

View File

@ -0,0 +1,40 @@
'use strict';
// This tests that --cpu-prof generates CPU profile when event
// loop is drained.
// TODO(joyeecheung): share the fixtures with v8 coverage tests
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
const profiles = getCpuProfiles(tmpdir.path);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci.js');
}

View File

@ -0,0 +1,39 @@
'use strict';
// This tests that --cpu-prof generates CPU profile when
// process.exit(55) exits the process.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
fixtures.path('workload', 'fibonacci-exit.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 55) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 55);
const profiles = getCpuProfiles(tmpdir.path);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci-exit.js');
}

View File

@ -0,0 +1,79 @@
'use strict';
// This tests that invalid --cpu-prof options are rejected.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
kCpuProfInterval,
env
} = require('../common/cpu-prof');
// --cpu-prof-name without --cpu-prof
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof-name',
'test.cpuprofile',
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
const stderr = output.stderr.toString().trim();
if (output.status !== 9) {
console.log(stderr);
}
assert.strictEqual(output.status, 9);
assert.strictEqual(
stderr,
`${process.execPath}: --cpu-prof-name must be used with --cpu-prof`);
}
// --cpu-prof-dir without --cpu-prof
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof-dir',
'prof',
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
const stderr = output.stderr.toString().trim();
if (output.status !== 9) {
console.log(stderr);
}
assert.strictEqual(output.status, 9);
assert.strictEqual(
stderr,
`${process.execPath}: --cpu-prof-dir must be used with --cpu-prof`);
}
// --cpu-prof-interval without --cpu-prof
for (const arg of [kCpuProfInterval, 'crashme']) {
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof-interval',
arg,
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
const stderr = output.stderr.toString().trim();
if (output.status !== 9) {
console.log(stderr);
}
assert.strictEqual(output.status, 9);
assert.strictEqual(
stderr,
`${process.execPath}: --cpu-prof-interval must be used with --cpu-prof`);
}

View File

@ -0,0 +1,41 @@
'use strict';
// This tests that --cpu-prof generates CPU profile when
// process.kill(process.pid, "SIGINT"); exits the process.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
fixtures.path('workload', 'fibonacci-sigint.js'),
], {
cwd: tmpdir.path,
env
});
if (!common.isWindows) {
if (output.signal !== 'SIGINT') {
console.log(output.stderr.toString());
}
assert.strictEqual(output.signal, 'SIGINT');
}
const profiles = getCpuProfiles(tmpdir.path);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci-sigint.js');
}

View File

@ -0,0 +1,43 @@
'use strict';
// This tests that --cpu-prof-name can be used to specify the
// name of the generated CPU profile.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
// --cpu-prof-name
{
tmpdir.refresh();
const file = tmpdir.resolve('test.cpuprofile');
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
'--cpu-prof-name',
'test.cpuprofile',
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
const profiles = getCpuProfiles(tmpdir.path);
assert.deepStrictEqual(profiles, [file]);
verifyFrames(output, file, 'fibonacci.js');
}

View File

@ -0,0 +1,38 @@
'use strict';
// This tests that --cpu-prof generates CPU profile from worker
// when execArgv is set.
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
verifyFrames
} = require('../common/cpu-prof');
{
tmpdir.refresh();
const output = spawnSync(process.execPath, [
fixtures.path('workload', 'fibonacci-worker-argv.js'),
], {
cwd: tmpdir.path,
env: {
...process.env,
CPU_PROF_INTERVAL: kCpuProfInterval
}
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
const profiles = getCpuProfiles(tmpdir.path);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci.js');
}

View File

@ -0,0 +1,119 @@
// Flags: --expose-internals --no-warnings --allow-natives-syntax
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const crypto = require('crypto');
// 'should consider equal strings to be equal'
assert.strictEqual(
crypto.timingSafeEqual(Buffer.from('foo'), Buffer.from('foo')),
true
);
// 'should consider unequal strings to be unequal'
assert.strictEqual(
crypto.timingSafeEqual(Buffer.from('foo'), Buffer.from('bar')),
false
);
{
// Test TypedArrays with different lengths but equal byteLengths.
const buf = crypto.randomBytes(16).buffer;
const a1 = new Uint8Array(buf);
const a2 = new Uint16Array(buf);
const a3 = new Uint32Array(buf);
for (const left of [a1, a2, a3]) {
for (const right of [a1, a2, a3]) {
assert.strictEqual(crypto.timingSafeEqual(left, right), true);
}
}
}
{
// When the inputs are floating-point numbers, timingSafeEqual neither has
// equality nor SameValue semantics. It just compares the underlying bytes,
// ignoring the TypedArray type completely.
const cmp = (fn) => (a, b) => a.every((x, i) => fn(x, b[i]));
const eq = cmp((a, b) => a === b);
const is = cmp(Object.is);
function test(a, b, { equal, sameValue, timingSafeEqual }) {
assert.strictEqual(eq(a, b), equal);
assert.strictEqual(is(a, b), sameValue);
assert.strictEqual(crypto.timingSafeEqual(a, b), timingSafeEqual);
}
test(new Float32Array([NaN]), new Float32Array([NaN]), {
equal: false,
sameValue: true,
timingSafeEqual: true
});
test(new Float64Array([0]), new Float64Array([-0]), {
equal: true,
sameValue: false,
timingSafeEqual: false
});
const x = new BigInt64Array([0x7ff0000000000001n, 0xfff0000000000001n]);
test(new Float64Array(x.buffer), new Float64Array([NaN, NaN]), {
equal: false,
sameValue: true,
timingSafeEqual: false
});
}
assert.throws(
() => crypto.timingSafeEqual(Buffer.from([1, 2, 3]), Buffer.from([1, 2])),
{
code: 'ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
name: 'RangeError',
message: 'Input buffers must have the same byte length'
}
);
assert.throws(
() => crypto.timingSafeEqual('not a buffer', Buffer.from([1, 2])),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
}
);
assert.throws(
() => crypto.timingSafeEqual(Buffer.from([1, 2]), 'not a buffer'),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
}
);
{
// V8 Fast API
const foo = Buffer.from('foo');
const bar = Buffer.from('bar');
const longer = Buffer.from('longer');
function testFastPath(buf1, buf2) {
return crypto.timingSafeEqual(buf1, buf2);
}
eval('%PrepareFunctionForOptimization(testFastPath)');
assert.strictEqual(testFastPath(foo, bar), false);
eval('%OptimizeFunctionOnNextCall(testFastPath)');
assert.strictEqual(testFastPath(foo, bar), false);
assert.strictEqual(testFastPath(foo, foo), true);
assert.throws(() => testFastPath(foo, longer), {
code: 'ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
});
if (common.isDebug) {
const { internalBinding } = require('internal/test/binding');
const { getV8FastApiCallCount } = internalBinding('debug');
assert.strictEqual(getV8FastApiCallCount('crypto.timingSafeEqual.ok'), 2);
assert.strictEqual(getV8FastApiCallCount('crypto.timingSafeEqual.error'), 1);
}
}

View File

@ -0,0 +1,18 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const spawn = require('child_process').spawn;
const proc = spawn(process.execPath, ['inspect', 'foo']);
proc.stdout.setEncoding('utf8');
let needToSendExit = true;
let output = '';
proc.stdout.on('data', (data) => {
output += data;
if (output.includes('debug> ') && needToSendExit) {
proc.stdin.write('.exit\n');
needToSendExit = false;
}
});

View File

@ -0,0 +1,28 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const fixtures = require('../common/fixtures');
const startCLI = require('../common/debugger');
const assert = require('assert');
// Custom port.
const script = fixtures.path('debugger', 'three-lines.js');
const cli = startCLI([`--port=${common.PORT}`, script]);
(async function() {
try {
await cli.waitForInitialBreak();
await cli.waitForPrompt();
assert.match(cli.output, /debug>/, 'prints a prompt');
assert.match(
cli.output,
new RegExp(`< Debugger listening on [^\n]*${common.PORT}`),
'forwards child output');
} finally {
const code = await cli.quit();
assert.strictEqual(code, 0);
}
})().then(common.mustCall());

View File

@ -0,0 +1,21 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
// This test ensures that the --debug-brk flag will exit the process
const assert = require('assert');
const fixtures = require('../common/fixtures');
const { spawnSync } = require('child_process');
// File name here doesn't actually matter the process will exit on start.
const script = fixtures.path('empty.js');
function test(arg) {
const child = spawnSync(process.execPath, ['--inspect', arg, script]);
const stderr = child.stderr.toString();
assert(stderr.includes('DEP0062'));
assert.strictEqual(child.status, 9);
}
test('--debug-brk');
test('--debug-brk=5959');

View File

@ -0,0 +1,27 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const startCLI = require('../common/debugger');
const assert = require('assert');
// Launch CLI w/o args.
(async () => {
const cli = startCLI([]);
const code = await cli.quit();
assert.strictEqual(code, 9);
assert.match(cli.output, /^Usage:/, 'Prints usage info');
})().then(common.mustCall());
// Launch w/ invalid host:port.
(async () => {
const cli = startCLI([`localhost:${common.PORT}`]);
const code = await cli.quit();
assert.match(
cli.output,
/failed to connect/,
'Tells the user that the connection failed');
assert.strictEqual(code, 1);
})().then(common.mustCall());

View File

@ -0,0 +1,37 @@
import { skipIfInspectorDisabled } from '../common/index.mjs';
skipIfInspectorDisabled();
// This must be in sequential because we check that the default port is 9229.
import { path } from '../common/fixtures.mjs';
import startCLI from '../common/debugger.js';
import assert from 'assert';
const script = path('debugger', 'three-lines.js');
const cli = startCLI([script]);
try {
await cli.waitForInitialBreak();
await cli.waitForPrompt();
assert.match(cli.output, /debug>/, 'prints a prompt');
assert.match(
cli.output,
/< Debugger listening on [^\n]*9229/,
'forwards child output'
);
await cli.command('["hello", "world"].join(" ")');
assert.match(cli.output, /hello world/, 'prints the result');
await cli.command('');
assert.match(
cli.output,
/hello world/,
'repeats the last command on <enter>'
);
await cli.command('version');
assert.ok(
cli.output.includes(process.versions.v8),
'version prints the v8 version'
);
} finally {
const code = await cli.quit();
assert.strictEqual(code, 0);
}

View File

@ -0,0 +1,35 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const fixtures = require('../common/fixtures');
const startCLI = require('../common/debugger');
const assert = require('assert');
const { spawn } = require('child_process');
const script = fixtures.path('debugger', 'alive.js');
const runTest = async () => {
const target = spawn(process.execPath, [script]);
const cli = startCLI(['-p', `${target.pid}`]);
try {
await cli.waitForPrompt();
await cli.command('sb("alive.js", 3)');
await cli.waitFor(/break/);
await cli.waitForPrompt();
assert.match(
cli.output,
/> 3 {3}\+\+x;/,
'marks the 3rd line');
} catch (error) {
assert.ifError(error);
} finally {
await cli.quit();
target.kill();
}
};
runTest().then(common.mustCall());

View File

@ -0,0 +1,88 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const execFile = require('child_process').execFile;
const depmod = fixtures.path('deprecated.js');
const node = process.execPath;
const depUserlandFunction =
fixtures.path('deprecated-userland-function.js');
const depUserlandClass =
fixtures.path('deprecated-userland-class.js');
const depUserlandSubClass =
fixtures.path('deprecated-userland-subclass.js');
const normal = [depmod];
const noDep = ['--no-deprecation', depmod];
const traceDep = ['--trace-deprecation', depmod];
execFile(node, normal, function(er, stdout, stderr) {
console.error('normal: show deprecation warning');
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.match(stderr, /this function is deprecated/);
console.log('normal ok');
});
execFile(node, noDep, function(er, stdout, stderr) {
console.error('--no-deprecation: silence deprecations');
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.strictEqual(stderr.trim(), 'This is deprecated');
console.log('silent ok');
});
execFile(node, traceDep, function(er, stdout, stderr) {
console.error('--trace-deprecation: show stack');
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
const stack = stderr.trim().split('\n');
// Just check the top and bottom.
assert.match(stack[1], /this function is deprecated/);
assert.match(stack[0], /This is deprecated/);
console.log('trace ok');
});
execFile(node, [depUserlandFunction], function(er, stdout, stderr) {
console.error('normal: testing deprecated userland function');
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.match(stderr, /deprecatedFunction is deprecated/);
console.error('normal: ok');
});
execFile(node, [depUserlandClass], function(er, stdout, stderr) {
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.match(stderr, /deprecatedClass is deprecated/);
});
execFile(node, [depUserlandSubClass], function(er, stdout, stderr) {
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.match(stderr, /deprecatedClass is deprecated/);
});

View File

@ -0,0 +1,113 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
// This test asserts the semantics of dgram::socket.bind({ exclusive })
// when called from a cluster.Worker
const assert = require('assert');
const cluster = require('cluster');
const dgram = require('dgram');
const BYE = 'bye';
const WORKER2_NAME = 'wrker2';
if (cluster.isPrimary) {
const worker1 = cluster.fork();
if (common.isWindows) {
worker1.on('error', common.mustCall((err) => {
console.log(err);
assert.strictEqual(err.code, 'ENOTSUP');
worker1.kill();
}));
return;
}
worker1.on('message', common.mustCall((msg) => {
console.log(msg);
assert.strictEqual(msg, 'success');
const worker2 = cluster.fork({ WORKER2_NAME });
worker2.on('message', common.mustCall((msg) => {
console.log(msg);
assert.strictEqual(msg, 'socket3:EADDRINUSE');
// finish test
worker1.send(BYE);
worker2.send(BYE);
}));
worker2.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(signal, null);
assert.strictEqual(code, 0);
}));
}));
worker1.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(signal, null);
assert.strictEqual(code, 0);
}));
// end primary code
} else {
// worker code
process.on('message', common.mustCallAtLeast((msg) => {
if (msg === BYE) process.exit(0);
}), 1);
const isSecondWorker = process.env.WORKER2_NAME === WORKER2_NAME;
const socket1 = dgram.createSocket('udp4', common.mustNotCall());
const socket2 = dgram.createSocket('udp4', common.mustNotCall());
const socket3 = dgram.createSocket('udp4', common.mustNotCall());
socket1.on('error', (err) => assert.fail(err));
socket2.on('error', (err) => assert.fail(err));
// First worker should bind, second should err
const socket3OnBind =
isSecondWorker ?
common.mustNotCall() :
common.mustCall(() => {
const port3 = socket3.address().port;
assert.strictEqual(typeof port3, 'number');
process.send('success');
});
// An error is expected only in the second worker
const socket3OnError =
!isSecondWorker ?
common.mustNotCall() :
common.mustCall((err) => {
process.send(`socket3:${err.code}`);
});
const address = common.localhostIPv4;
const opt1 = { address, port: 0, exclusive: false };
const opt2 = { address, port: common.PORT, exclusive: false };
const opt3 = { address, port: common.PORT + 1, exclusive: true };
socket1.bind(opt1, common.mustCall(() => {
const port1 = socket1.address().port;
assert.strictEqual(typeof port1, 'number');
socket2.bind(opt2, common.mustCall(() => {
const port2 = socket2.address().port;
assert.strictEqual(typeof port2, 'number');
socket3.on('error', socket3OnError);
socket3.bind(opt3, socket3OnBind);
}));
}));
}

View File

@ -0,0 +1,40 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
const assert = require('assert');
const EventEmitter = require('events');
const dgram = require('dgram');
const dns = require('dns');
const { kStateSymbol } = require('internal/dgram');
const mockError = new Error('fake DNS');
// Monkey patch dns.lookup() so that it always fails.
dns.lookup = function(address, family, callback) {
process.nextTick(() => { callback(mockError); });
};
const socket = dgram.createSocket('udp4');
socket.on(EventEmitter.errorMonitor, common.mustCall((err) => {
// The DNS lookup should fail since it is monkey patched. At that point in
// time, the send queue should be populated with the send() operation.
assert.strictEqual(err, mockError);
assert(Array.isArray(socket[kStateSymbol].queue));
assert.strictEqual(socket[kStateSymbol].queue.length, 1);
}, 3));
socket.on('error', common.mustCall((err) => {
assert.strictEqual(err, mockError);
assert.strictEqual(socket[kStateSymbol].queue, undefined);
}, 3));
// Initiate a few send() operations, which will fail.
socket.send('foobar', common.PORT, 'localhost');
process.nextTick(() => {
socket.send('foobar', common.PORT, 'localhost');
});
setImmediate(() => {
socket.send('foobar', common.PORT, 'localhost');
});

View File

@ -0,0 +1,46 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dgram = require('dgram');
function pingPongTest(port, host) {
const server = dgram.createSocket('udp4', common.mustCall((msg, rinfo) => {
assert.strictEqual(msg.toString('ascii'), 'PING');
server.send('PONG', 0, 4, rinfo.port, rinfo.address);
}));
server.on('error', function(e) {
throw e;
});
server.on('listening', function() {
console.log(`server listening on ${port}`);
const client = dgram.createSocket('udp4');
client.on('message', function(msg) {
assert.strictEqual(msg.toString('ascii'), 'PONG');
client.close();
server.close();
});
client.on('error', function(e) {
throw e;
});
console.log(`Client sending to ${port}`);
function clientSend() {
client.send('PING', 0, 4, port, 'localhost');
}
clientSend();
});
server.bind(port, host);
return server;
}
const server = pingPongTest(common.PORT, 'localhost');
server.on('close', common.mustCall(pingPongTest.bind(undefined, common.PORT)));

View File

@ -0,0 +1,75 @@
'use strict';
// This test is to ensure that --diagnostic-dir does not change the directory
// for --cpu-prof when --cpu-prof-dir is specified
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const fs = require('fs');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const {
getCpuProfiles,
kCpuProfInterval,
env,
verifyFrames
} = require('../common/cpu-prof');
// Test --diagnostic-dir changes the default for --cpu-prof
{
tmpdir.refresh();
const dir = tmpdir.resolve('prof');
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
'--diagnostic-dir',
dir,
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
assert(fs.existsSync(dir));
const profiles = getCpuProfiles(dir);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci.js');
}
// Test --cpu-prof-dir overwrites --diagnostic-dir
{
tmpdir.refresh();
const dir = tmpdir.resolve('diag');
const dir2 = tmpdir.resolve('prof');
const output = spawnSync(process.execPath, [
'--cpu-prof',
'--cpu-prof-interval',
kCpuProfInterval,
'--diagnostic-dir',
dir,
'--cpu-prof-dir',
dir2,
fixtures.path('workload', 'fibonacci.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
assert(fs.existsSync(dir2));
const profiles = getCpuProfiles(dir2);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'fibonacci.js');
}

View File

@ -0,0 +1,117 @@
'use strict';
// This test is to ensure that --diagnostic-dir does not change the directory
// for --cpu-prof when --cpu-prof-dir is specified
const common = require('../common');
const fixtures = require('../common/fixtures');
common.skipIfInspectorDisabled();
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
function findFirstFrameInNode(root, func) {
const first = root.children.find(
(child) => child.callFrame.functionName === func
);
if (first) {
return first;
}
for (const child of root.children) {
const first = findFirstFrameInNode(child, func);
if (first) {
return first;
}
}
return undefined;
}
function findFirstFrame(file, func) {
const data = fs.readFileSync(file, 'utf8');
const profile = JSON.parse(data);
const first = findFirstFrameInNode(profile.head, func);
return { frame: first, roots: profile.head.children };
}
function verifyFrames(output, file, func) {
const { frame, roots } = findFirstFrame(file, func);
if (!frame) {
// Show native debug output and the profile for debugging.
console.log(output.stderr.toString());
console.log(roots);
}
assert.notStrictEqual(frame, undefined);
}
const kHeapProfInterval = 128;
const TEST_ALLOCATION = kHeapProfInterval * 2;
const env = {
...process.env,
TEST_ALLOCATION,
NODE_DEBUG_NATIVE: 'INSPECTOR_PROFILER'
};
function getHeapProfiles(dir) {
const list = fs.readdirSync(dir);
return list
.filter((file) => file.endsWith('.heapprofile'))
.map((file) => path.join(dir, file));
}
// Test --diagnostic-dir changes the default for --cpu-prof
{
tmpdir.refresh();
const dir = tmpdir.resolve('prof');
const output = spawnSync(process.execPath, [
'--heap-prof',
'--diagnostic-dir',
dir,
'--heap-prof-interval',
kHeapProfInterval,
fixtures.path('workload', 'allocation.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
assert(fs.existsSync(dir));
const profiles = getHeapProfiles(dir);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'runAllocation');
}
// Test --heap-prof-dir overwrites --diagnostic-dir
{
tmpdir.refresh();
const dir = tmpdir.resolve('diag');
const dir2 = tmpdir.resolve('prof');
const output = spawnSync(process.execPath, [
'--heap-prof',
'--heap-prof-interval',
kHeapProfInterval,
'--diagnostic-dir',
dir,
'--heap-prof-dir',
dir2,
fixtures.path('workload', 'allocation.js'),
], {
cwd: tmpdir.path,
env
});
if (output.status !== 0) {
console.log(output.stderr.toString());
}
assert.strictEqual(output.status, 0);
assert(fs.existsSync(dir2));
const profiles = getHeapProfiles(dir2);
assert.strictEqual(profiles.length, 1);
verifyFrames(output, profiles[0], 'runAllocation');
}

View File

@ -0,0 +1,139 @@
// Flags: --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const { inspect } = require('util');
const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes;
const { serializeError, deserializeError } = require('internal/error_serdes');
function cycle(err) {
return deserializeError(serializeError(err));
}
assert.strictEqual(cycle(0), 0);
assert.strictEqual(cycle(-1), -1);
assert.strictEqual(cycle(1.4), 1.4);
assert.strictEqual(cycle(null), null);
assert.strictEqual(cycle(undefined), undefined);
assert.strictEqual(cycle('foo'), 'foo');
assert.strictEqual(cycle(Symbol.for('foo')), Symbol.for('foo'));
assert.strictEqual(cycle(Symbol('foo')).toString(), Symbol('foo').toString());
let err = new Error('foo');
for (let i = 0; i < 10; i++) {
assert(err instanceof Error);
assert(Object.prototype.toString.call(err), '[object Error]');
assert.strictEqual(err.name, 'Error');
assert.strictEqual(err.message, 'foo');
assert.match(err.stack, /^Error: foo\n/);
const prev = err;
err = cycle(err);
assert.deepStrictEqual(err, prev);
}
assert.strictEqual(cycle(new RangeError('foo')).name, 'RangeError');
assert.strictEqual(cycle(new TypeError('foo')).name, 'TypeError');
assert.strictEqual(cycle(new ReferenceError('foo')).name, 'ReferenceError');
assert.strictEqual(cycle(new URIError('foo')).name, 'URIError');
assert.strictEqual(cycle(new EvalError('foo')).name, 'EvalError');
assert.strictEqual(cycle(new SyntaxError('foo')).name, 'SyntaxError');
class SubError extends Error {}
assert.strictEqual(cycle(new SubError('foo')).name, 'Error');
assert.deepStrictEqual(cycle({ message: 'foo' }), { message: 'foo' });
assert.strictEqual(cycle(Function), '[Function: Function]');
class ErrorWithCause extends Error {
get cause() {
return new Error('err');
}
}
class ErrorWithThowingCause extends Error {
get cause() {
throw new Error('err');
}
}
class ErrorWithCyclicCause extends Error {
get cause() {
return this;
}
}
const errorWithCause = Object
.defineProperty(new Error('Error with cause'), 'cause', { get() { return { foo: 'bar' }; } });
const errorWithThrowingCause = Object
.defineProperty(new Error('Error with cause'), 'cause', { get() { throw new Error('err'); } });
const errorWithCyclicCause = Object
.defineProperty(new Error('Error with cause'), 'cause', { get() { return errorWithCyclicCause; } });
assert.strictEqual(cycle(new Error('Error with cause', { cause: 0 })).cause, 0);
assert.strictEqual(cycle(new Error('Error with cause', { cause: -1 })).cause, -1);
assert.strictEqual(cycle(new Error('Error with cause', { cause: 1.4 })).cause, 1.4);
assert.strictEqual(cycle(new Error('Error with cause', { cause: null })).cause, null);
assert.strictEqual(cycle(new Error('Error with cause', { cause: undefined })).cause, undefined);
assert.strictEqual(Object.hasOwn(cycle(new Error('Error with cause', { cause: undefined })), 'cause'), true);
assert.strictEqual(cycle(new Error('Error with cause', { cause: 'foo' })).cause, 'foo');
assert.deepStrictEqual(cycle(new Error('Error with cause', { cause: new Error('err') })).cause, new Error('err'));
assert.deepStrictEqual(cycle(errorWithCause).cause, { foo: 'bar' });
assert.strictEqual(Object.hasOwn(cycle(errorWithThrowingCause), 'cause'), false);
assert.strictEqual(Object.hasOwn(cycle(errorWithCyclicCause), 'cause'), true);
assert.deepStrictEqual(cycle(new ErrorWithCause('Error with cause')).cause, new Error('err'));
assert.strictEqual(cycle(new ErrorWithThowingCause('Error with cause')).cause, undefined);
assert.strictEqual(Object.hasOwn(cycle(new ErrorWithThowingCause('Error with cause')), 'cause'), false);
// When the cause is cyclic, it is serialized as a dumb circular reference object.
let depth = 0;
let e = cycle(new ErrorWithCyclicCause('Error with cause'));
while (e.cause) {
e = e.cause;
depth++;
}
assert.strictEqual(depth, 1);
assert.strictEqual(
inspect(cycle(new ErrorWithCyclicCause('Error with cause')).cause),
'[Circular object]',
);
{
const err = new ERR_INVALID_ARG_TYPE('object', 'Object', 42);
assert.match(String(err), /^TypeError \[ERR_INVALID_ARG_TYPE\]:/);
assert.strictEqual(err.name, 'TypeError');
assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE');
}
{
let called = false;
class DynamicError extends Error {
get type() {
called = true;
return 'dynamic';
}
get shouldIgnoreError() {
throw new Error();
}
}
serializeError(new DynamicError());
assert.strictEqual(called, true);
}
const data = {
foo: 'bar',
[inspect.custom]() {
return 'barbaz';
}
};
assert.strictEqual(inspect(cycle(data)), 'barbaz');
const inheritedCustomInspect = new class {
foo = 'bar';
[inspect.custom]() {
return 'barbaz';
}
}();
assert.strictEqual(inspect(cycle(inheritedCustomInspect)), 'barbaz');

View File

@ -0,0 +1,242 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const fsPromises = fs.promises;
const pathModule = require('path');
const tmpdir = require('../common/tmpdir');
const testDir = tmpdir.path;
const fileStructure = [
[ 'a', [ 'a', 'foo', 'bar' ] ],
[ 'b', [ 'foo', 'bar' ] ],
[ 'c', [ 'foo', 'bar' ] ],
[ 'd', [ 'foo', 'bar' ] ],
[ 'e', [ 'foo', 'bar' ] ],
[ 'f', [ 'foo', 'bar' ] ],
[ 'g', [ 'foo', 'bar' ] ],
[ 'h', [ 'foo', 'bar' ] ],
[ 'i', [ 'foo', 'bar' ] ],
[ 'j', [ 'foo', 'bar' ] ],
[ 'k', [ 'foo', 'bar' ] ],
[ 'l', [ 'foo', 'bar' ] ],
[ 'm', [ 'foo', 'bar' ] ],
[ 'n', [ 'foo', 'bar' ] ],
[ 'o', [ 'foo', 'bar' ] ],
[ 'p', [ 'foo', 'bar' ] ],
[ 'q', [ 'foo', 'bar' ] ],
[ 'r', [ 'foo', 'bar' ] ],
[ 's', [ 'foo', 'bar' ] ],
[ 't', [ 'foo', 'bar' ] ],
[ 'u', [ 'foo', 'bar' ] ],
[ 'v', [ 'foo', 'bar' ] ],
[ 'w', [ 'foo', 'bar' ] ],
[ 'x', [ 'foo', 'bar' ] ],
[ 'y', [ 'foo', 'bar' ] ],
[ 'z', [ 'foo', 'bar' ] ],
[ 'aa', [ 'foo', 'bar' ] ],
[ 'bb', [ 'foo', 'bar' ] ],
[ 'cc', [ 'foo', 'bar' ] ],
[ 'dd', [ 'foo', 'bar' ] ],
[ 'ee', [ 'foo', 'bar' ] ],
[ 'ff', [ 'foo', 'bar' ] ],
[ 'gg', [ 'foo', 'bar' ] ],
[ 'hh', [ 'foo', 'bar' ] ],
[ 'ii', [ 'foo', 'bar' ] ],
[ 'jj', [ 'foo', 'bar' ] ],
[ 'kk', [ 'foo', 'bar' ] ],
[ 'll', [ 'foo', 'bar' ] ],
[ 'mm', [ 'foo', 'bar' ] ],
[ 'nn', [ 'foo', 'bar' ] ],
[ 'oo', [ 'foo', 'bar' ] ],
[ 'pp', [ 'foo', 'bar' ] ],
[ 'qq', [ 'foo', 'bar' ] ],
[ 'rr', [ 'foo', 'bar' ] ],
[ 'ss', [ 'foo', 'bar' ] ],
[ 'tt', [ 'foo', 'bar' ] ],
[ 'uu', [ 'foo', 'bar' ] ],
[ 'vv', [ 'foo', 'bar' ] ],
[ 'ww', [ 'foo', 'bar' ] ],
[ 'xx', [ 'foo', 'bar' ] ],
[ 'yy', [ 'foo', 'bar' ] ],
[ 'zz', [ 'foo', 'bar' ] ],
[ 'abc', [ ['def', [ 'foo', 'bar' ] ], ['ghi', [ 'foo', 'bar' ] ] ] ],
];
function createFiles(path, fileStructure) {
for (const fileOrDir of fileStructure) {
if (typeof fileOrDir === 'string') {
fs.writeFileSync(pathModule.join(path, fileOrDir), '');
} else {
const dirPath = pathModule.join(path, fileOrDir[0]);
fs.mkdirSync(dirPath);
createFiles(dirPath, fileOrDir[1]);
}
}
}
// Make sure tmp directory is clean
tmpdir.refresh();
createFiles(testDir, fileStructure);
const symlinksRootPath = pathModule.join(testDir, 'symlinks');
const symlinkTargetFile = pathModule.join(symlinksRootPath, 'symlink-target-file');
const symlinkTargetDir = pathModule.join(symlinksRootPath, 'symlink-target-dir');
fs.mkdirSync(symlinksRootPath);
fs.writeFileSync(symlinkTargetFile, '');
fs.mkdirSync(symlinkTargetDir);
fs.symlinkSync(symlinkTargetFile, pathModule.join(symlinksRootPath, 'symlink-src-file'));
fs.symlinkSync(symlinkTargetDir, pathModule.join(symlinksRootPath, 'symlink-src-dir'));
const expected = [
'a', 'a/a', 'a/bar', 'a/foo', 'aa', 'aa/bar', 'aa/foo',
'abc', 'abc/def', 'abc/def/bar', 'abc/def/foo', 'abc/ghi', 'abc/ghi/bar', 'abc/ghi/foo',
'b', 'b/bar', 'b/foo', 'bb', 'bb/bar', 'bb/foo',
'c', 'c/bar', 'c/foo', 'cc', 'cc/bar', 'cc/foo',
'd', 'd/bar', 'd/foo', 'dd', 'dd/bar', 'dd/foo',
'e', 'e/bar', 'e/foo', 'ee', 'ee/bar', 'ee/foo',
'f', 'f/bar', 'f/foo', 'ff', 'ff/bar', 'ff/foo',
'g', 'g/bar', 'g/foo', 'gg', 'gg/bar', 'gg/foo',
'h', 'h/bar', 'h/foo', 'hh', 'hh/bar', 'hh/foo',
'i', 'i/bar', 'i/foo', 'ii', 'ii/bar', 'ii/foo',
'j', 'j/bar', 'j/foo', 'jj', 'jj/bar', 'jj/foo',
'k', 'k/bar', 'k/foo', 'kk', 'kk/bar', 'kk/foo',
'l', 'l/bar', 'l/foo', 'll', 'll/bar', 'll/foo',
'm', 'm/bar', 'm/foo', 'mm', 'mm/bar', 'mm/foo',
'n', 'n/bar', 'n/foo', 'nn', 'nn/bar', 'nn/foo',
'o', 'o/bar', 'o/foo', 'oo', 'oo/bar', 'oo/foo',
'p', 'p/bar', 'p/foo', 'pp', 'pp/bar', 'pp/foo',
'q', 'q/bar', 'q/foo', 'qq', 'qq/bar', 'qq/foo',
'r', 'r/bar', 'r/foo', 'rr', 'rr/bar', 'rr/foo',
's', 's/bar', 's/foo', 'ss', 'ss/bar', 'ss/foo',
'symlinks', 'symlinks/symlink-src-dir', 'symlinks/symlink-src-file',
'symlinks/symlink-target-dir', 'symlinks/symlink-target-file',
't', 't/bar', 't/foo', 'tt', 'tt/bar', 'tt/foo',
'u', 'u/bar', 'u/foo', 'uu', 'uu/bar', 'uu/foo',
'v', 'v/bar', 'v/foo', 'vv', 'vv/bar', 'vv/foo',
'w', 'w/bar', 'w/foo', 'ww', 'ww/bar', 'ww/foo',
'x', 'x/bar', 'x/foo', 'xx', 'xx/bar', 'xx/foo',
'y', 'y/bar', 'y/foo', 'yy', 'yy/bar', 'yy/foo',
'z', 'z/bar', 'z/foo', 'zz', 'zz/bar', 'zz/foo',
];
// Normalize paths once for non POSIX platforms
for (let i = 0; i < expected.length; i++) {
expected[i] = pathModule.normalize(expected[i]);
}
function getDirentPath(dirent) {
return pathModule.relative(testDir, pathModule.join(dirent.parentPath, dirent.name));
}
function assertDirents(dirents) {
assert.strictEqual(dirents.length, expected.length);
dirents.sort((a, b) => (getDirentPath(a) < getDirentPath(b) ? -1 : 1));
assert.deepStrictEqual(
dirents.map((dirent) => {
assert(dirent instanceof fs.Dirent);
return getDirentPath(dirent);
}),
expected
);
}
function processDirSync(dir) {
const dirents = [];
let dirent = dir.readSync();
while (dirent !== null) {
dirents.push(dirent);
dirent = dir.readSync();
}
assertDirents(dirents);
}
// Opendir read results sync
{
const dir = fs.opendirSync(testDir, { recursive: true });
processDirSync(dir);
dir.closeSync();
}
{
fs.opendir(testDir, { recursive: true }, common.mustSucceed((dir) => {
processDirSync(dir);
dir.close(common.mustSucceed());
}));
}
// Opendir read result using callback
function processDirCb(dir, cb) {
const acc = [];
function _process(dir, acc, cb) {
dir.read((err, dirent) => {
if (err) {
return cb(err);
}
if (dirent !== null) {
acc.push(dirent);
_process(dir, acc, cb);
} else {
cb(null, acc);
}
});
}
_process(dir, acc, cb);
}
{
const dir = fs.opendirSync(testDir, { recursive: true });
processDirCb(dir, common.mustSucceed((dirents) => {
assertDirents(dirents);
dir.close(common.mustSucceed());
}));
}
{
fs.opendir(testDir, { recursive: true }, common.mustSucceed((dir) => {
processDirCb(dir, common.mustSucceed((dirents) => {
assertDirents(dirents);
dir.close(common.mustSucceed());
}));
}));
}
// Opendir read result using AsyncIterator
{
async function test() {
const dir = await fsPromises.opendir(testDir, { recursive: true });
const dirents = [];
for await (const dirent of dir) {
dirents.push(dirent);
}
assertDirents(dirents);
}
test().then(common.mustCall());
}
// Issue https://github.com/nodejs/node/issues/48820 highlights that
// opendir recursive does not properly handle the buffer size option.
// This test asserts that the buffer size option is respected.
{
const dir = fs.opendirSync(testDir, { bufferSize: 1, recursive: true });
processDirSync(dir);
dir.closeSync();
}
{
fs.opendir(testDir, { recursive: true, bufferSize: 1 }, common.mustSucceed((dir) => {
processDirCb(dir, common.mustSucceed((dirents) => {
assertDirents(dirents);
dir.close(common.mustSucceed());
}));
}));
}

View File

@ -0,0 +1,198 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const pathModule = require('path');
const tmpdir = require('../common/tmpdir');
const readdirDir = tmpdir.path;
const fileStructure = [
[ 'a', [ 'a', 'foo', 'bar' ] ],
[ 'b', [ 'foo', 'bar' ] ],
[ 'c', [ 'foo', 'bar' ] ],
[ 'd', [ 'foo', 'bar' ] ],
[ 'e', [ 'foo', 'bar' ] ],
[ 'f', [ 'foo', 'bar' ] ],
[ 'g', [ 'foo', 'bar' ] ],
[ 'h', [ 'foo', 'bar' ] ],
[ 'i', [ 'foo', 'bar' ] ],
[ 'j', [ 'foo', 'bar' ] ],
[ 'k', [ 'foo', 'bar' ] ],
[ 'l', [ 'foo', 'bar' ] ],
[ 'm', [ 'foo', 'bar' ] ],
[ 'n', [ 'foo', 'bar' ] ],
[ 'o', [ 'foo', 'bar' ] ],
[ 'p', [ 'foo', 'bar' ] ],
[ 'q', [ 'foo', 'bar' ] ],
[ 'r', [ 'foo', 'bar' ] ],
[ 's', [ 'foo', 'bar' ] ],
[ 't', [ 'foo', 'bar' ] ],
[ 'u', [ 'foo', 'bar' ] ],
[ 'v', [ 'foo', 'bar' ] ],
[ 'w', [ 'foo', 'bar' ] ],
[ 'x', [ 'foo', 'bar' ] ],
[ 'y', [ 'foo', 'bar' ] ],
[ 'z', [ 'foo', 'bar' ] ],
[ 'aa', [ 'foo', 'bar' ] ],
[ 'bb', [ 'foo', 'bar' ] ],
[ 'cc', [ 'foo', 'bar' ] ],
[ 'dd', [ 'foo', 'bar' ] ],
[ 'ee', [ 'foo', 'bar' ] ],
[ 'ff', [ 'foo', 'bar' ] ],
[ 'gg', [ 'foo', 'bar' ] ],
[ 'hh', [ 'foo', 'bar' ] ],
[ 'ii', [ 'foo', 'bar' ] ],
[ 'jj', [ 'foo', 'bar' ] ],
[ 'kk', [ 'foo', 'bar' ] ],
[ 'll', [ 'foo', 'bar' ] ],
[ 'mm', [ 'foo', 'bar' ] ],
[ 'nn', [ 'foo', 'bar' ] ],
[ 'oo', [ 'foo', 'bar' ] ],
[ 'pp', [ 'foo', 'bar' ] ],
[ 'qq', [ 'foo', 'bar' ] ],
[ 'rr', [ 'foo', 'bar' ] ],
[ 'ss', [ 'foo', 'bar' ] ],
[ 'tt', [ 'foo', 'bar' ] ],
[ 'uu', [ 'foo', 'bar' ] ],
[ 'vv', [ 'foo', 'bar' ] ],
[ 'ww', [ 'foo', 'bar' ] ],
[ 'xx', [ 'foo', 'bar' ] ],
[ 'yy', [ 'foo', 'bar' ] ],
[ 'zz', [ 'foo', 'bar' ] ],
[ 'abc', [ ['def', [ 'foo', 'bar' ] ], ['ghi', [ 'foo', 'bar' ] ] ] ],
];
function createFiles(path, fileStructure) {
for (const fileOrDir of fileStructure) {
if (typeof fileOrDir === 'string') {
fs.writeFileSync(pathModule.join(path, fileOrDir), '');
} else {
const dirPath = pathModule.join(path, fileOrDir[0]);
fs.mkdirSync(dirPath);
createFiles(dirPath, fileOrDir[1]);
}
}
}
// Make sure tmp directory is clean
tmpdir.refresh();
createFiles(readdirDir, fileStructure);
const symlinksRootPath = pathModule.join(readdirDir, 'symlinks');
const symlinkTargetFile = pathModule.join(symlinksRootPath, 'symlink-target-file');
const symlinkTargetDir = pathModule.join(symlinksRootPath, 'symlink-target-dir');
fs.mkdirSync(symlinksRootPath);
fs.writeFileSync(symlinkTargetFile, '');
fs.mkdirSync(symlinkTargetDir);
fs.symlinkSync(symlinkTargetFile, pathModule.join(symlinksRootPath, 'symlink-src-file'));
fs.symlinkSync(symlinkTargetDir, pathModule.join(symlinksRootPath, 'symlink-src-dir'));
const expected = [
'a', 'a/a', 'a/bar', 'a/foo', 'aa', 'aa/bar', 'aa/foo',
'abc', 'abc/def', 'abc/def/bar', 'abc/def/foo', 'abc/ghi', 'abc/ghi/bar', 'abc/ghi/foo',
'b', 'b/bar', 'b/foo', 'bb', 'bb/bar', 'bb/foo',
'c', 'c/bar', 'c/foo', 'cc', 'cc/bar', 'cc/foo',
'd', 'd/bar', 'd/foo', 'dd', 'dd/bar', 'dd/foo',
'e', 'e/bar', 'e/foo', 'ee', 'ee/bar', 'ee/foo',
'f', 'f/bar', 'f/foo', 'ff', 'ff/bar', 'ff/foo',
'g', 'g/bar', 'g/foo', 'gg', 'gg/bar', 'gg/foo',
'h', 'h/bar', 'h/foo', 'hh', 'hh/bar', 'hh/foo',
'i', 'i/bar', 'i/foo', 'ii', 'ii/bar', 'ii/foo',
'j', 'j/bar', 'j/foo', 'jj', 'jj/bar', 'jj/foo',
'k', 'k/bar', 'k/foo', 'kk', 'kk/bar', 'kk/foo',
'l', 'l/bar', 'l/foo', 'll', 'll/bar', 'll/foo',
'm', 'm/bar', 'm/foo', 'mm', 'mm/bar', 'mm/foo',
'n', 'n/bar', 'n/foo', 'nn', 'nn/bar', 'nn/foo',
'o', 'o/bar', 'o/foo', 'oo', 'oo/bar', 'oo/foo',
'p', 'p/bar', 'p/foo', 'pp', 'pp/bar', 'pp/foo',
'q', 'q/bar', 'q/foo', 'qq', 'qq/bar', 'qq/foo',
'r', 'r/bar', 'r/foo', 'rr', 'rr/bar', 'rr/foo',
's', 's/bar', 's/foo', 'ss', 'ss/bar', 'ss/foo',
'symlinks', 'symlinks/symlink-src-dir', 'symlinks/symlink-src-file',
'symlinks/symlink-target-dir', 'symlinks/symlink-target-file',
't', 't/bar', 't/foo', 'tt', 'tt/bar', 'tt/foo',
'u', 'u/bar', 'u/foo', 'uu', 'uu/bar', 'uu/foo',
'v', 'v/bar', 'v/foo', 'vv', 'vv/bar', 'vv/foo',
'w', 'w/bar', 'w/foo', 'ww', 'ww/bar', 'ww/foo',
'x', 'x/bar', 'x/foo', 'xx', 'xx/bar', 'xx/foo',
'y', 'y/bar', 'y/foo', 'yy', 'yy/bar', 'yy/foo',
'z', 'z/bar', 'z/foo', 'zz', 'zz/bar', 'zz/foo',
];
// Normalize paths once for non POSIX platforms
for (let i = 0; i < expected.length; i++) {
expected[i] = pathModule.normalize(expected[i]);
}
function getDirentPath(dirent) {
return pathModule.relative(readdirDir, pathModule.join(dirent.parentPath, dirent.name));
}
function assertDirents(dirents) {
assert.strictEqual(dirents.length, expected.length);
dirents.sort((a, b) => (getDirentPath(a) < getDirentPath(b) ? -1 : 1));
assert.deepStrictEqual(
dirents.map((dirent) => {
assert(dirent instanceof fs.Dirent);
assert.notStrictEqual(dirent.name, undefined);
return getDirentPath(dirent);
}),
expected
);
}
// readdirSync
// readdirSync { recursive }
{
const result = fs.readdirSync(readdirDir, { recursive: true });
assert.deepStrictEqual(result.sort(), expected);
}
// readdirSync { recursive, withFileTypes }
{
const result = fs.readdirSync(readdirDir, { recursive: true, withFileTypes: true });
assertDirents(result);
}
// readdir
// readdir { recursive } callback
{
fs.readdir(readdirDir, { recursive: true },
common.mustSucceed((result) => {
assert.deepStrictEqual(result.sort(), expected);
}));
}
// Readdir { recursive, withFileTypes } callback
{
fs.readdir(readdirDir, { recursive: true, withFileTypes: true },
common.mustSucceed((result) => {
assertDirents(result);
}));
}
// fs.promises.readdir
// fs.promises.readdir { recursive }
{
async function test() {
const result = await fs.promises.readdir(readdirDir, { recursive: true });
assert.deepStrictEqual(result.sort(), expected);
}
test().then(common.mustCall());
}
// fs.promises.readdir { recursive, withFileTypes }
{
async function test() {
const result = await fs.promises.readdir(readdirDir, { recursive: true, withFileTypes: true });
assertDirents(result);
}
test().then(common.mustCall());
}

View File

@ -0,0 +1,43 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const fixtures = require('../common/fixtures');
// Check that the calls to Integer::New() and Date::New() succeed and bail out
// if they don't.
// V8 returns an empty handle on stack overflow. Trying to set the empty handle
// as a property on an object results in a NULL pointer dereference in release
// builds and an assert in debug builds.
// https://github.com/nodejs/node-v0.x-archive/issues/4015
const assert = require('assert');
const { spawn } = require('child_process');
const cp = spawn(process.execPath, [fixtures.path('test-fs-stat-sync-overflow.js')]);
const stderr = [];
cp.stderr.on('data', (chunk) => stderr.push(chunk));
cp.on('exit', common.mustCall(() => {
assert.match(Buffer.concat(stderr).toString('utf8'), /RangeError: Maximum call stack size exceeded/);
}));

View File

@ -0,0 +1,180 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
if (common.isIBMi)
common.skip('IBMi does not support fs.watch()');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const tmpdir = require('../common/tmpdir');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('process.chdir is not available in Workers');
}
const expectFilePath = common.isWindows ||
common.isLinux ||
common.isMacOS ||
common.isAIX;
const testDir = tmpdir.path;
tmpdir.refresh();
// Because macOS (and possibly other operating systems) can return a watcher
// before it is actually watching, we need to repeat the operation to avoid
// a race condition.
function repeat(fn) {
setImmediate(fn);
const interval = setInterval(fn, 5000);
return interval;
}
{
const filepath = path.join(testDir, 'watch.txt');
fs.writeFileSync(filepath, 'hello');
const watcher = fs.watch(filepath);
watcher.on('change', common.mustCall(function(event, filename) {
assert.strictEqual(event, 'change');
if (expectFilePath) {
assert.strictEqual(filename, 'watch.txt');
}
clearInterval(interval);
watcher.close();
}));
const interval = repeat(() => { fs.writeFileSync(filepath, 'world'); });
}
{
const filepathAbs = path.join(testDir, 'hasOwnProperty');
process.chdir(testDir);
fs.writeFileSync(filepathAbs, 'howdy');
const watcher =
fs.watch('hasOwnProperty', common.mustCall(function(event, filename) {
assert.strictEqual(event, 'change');
if (expectFilePath) {
assert.strictEqual(filename, 'hasOwnProperty');
}
clearInterval(interval);
watcher.close();
}));
const interval = repeat(() => { fs.writeFileSync(filepathAbs, 'pardner'); });
}
{
const testsubdir = fs.mkdtempSync(testDir + path.sep);
const filepath = path.join(testsubdir, 'newfile.txt');
function doWatch() {
const watcher =
fs.watch(testsubdir, common.mustCall(function(event, filename) {
const renameEv = common.isSunOS || common.isAIX ? 'change' : 'rename';
assert.strictEqual(event, renameEv);
if (expectFilePath) {
assert.strictEqual(filename, 'newfile.txt');
} else {
assert.strictEqual(filename, null);
}
clearInterval(interval);
watcher.close();
}));
const interval = repeat(() => {
fs.rmSync(filepath, { force: true });
const fd = fs.openSync(filepath, 'w');
fs.closeSync(fd);
});
}
if (common.isMacOS) {
// On macOS delay watcher start to avoid leaking previous events.
// Refs: https://github.com/libuv/libuv/pull/4503
setTimeout(doWatch, common.platformTimeout(100));
} else {
doWatch();
}
}
// https://github.com/joyent/node/issues/2293 - non-persistent watcher should
// not block the event loop
{
fs.watch(__filename, { persistent: false }, common.mustNotCall());
}
// Whitebox test to ensure that wrapped FSEvent is safe
// https://github.com/joyent/node/issues/6690
{
if (common.isMacOS || common.isWindows) {
let oldhandle;
assert.throws(
() => {
const w = fs.watch(__filename, common.mustNotCall());
oldhandle = w._handle;
w._handle = { close: w._handle.close };
w.close();
},
{
name: 'Error',
code: 'ERR_INTERNAL_ASSERTION',
message: /^handle must be a FSEvent/,
}
);
oldhandle.close(); // clean up
}
}
{
if (common.isMacOS || common.isWindows) {
let oldhandle;
assert.throws(
() => {
const w = fs.watch(__filename, common.mustNotCall());
oldhandle = w._handle;
const protoSymbols =
Object.getOwnPropertySymbols(Object.getPrototypeOf(w));
const kFSWatchStart =
protoSymbols.find((val) => val.toString() === 'Symbol(kFSWatchStart)');
w._handle = {};
w[kFSWatchStart]();
},
{
name: 'Error',
code: 'ERR_INTERNAL_ASSERTION',
message: /^handle must be a FSEvent/,
}
);
oldhandle.close(); // clean up
}
}

View File

@ -0,0 +1,77 @@
'use strict';
// Flags: --expose-gc
// just like test-gc-http-client.js,
// but with an on('error') handler that does nothing.
const common = require('../common');
const { onGC } = require('../common/gc');
const cpus = require('os').availableParallelism();
function serverHandler(req, res) {
req.resume();
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}
const http = require('http');
const numRequests = 36;
let createClients = true;
let done = 0;
let count = 0;
let countGC = 0;
const server = http.createServer(serverHandler);
server.listen(0, common.mustCall(() => {
for (let i = 0; i < cpus; i++)
getAll(numRequests);
}));
function getAll(requestsRemaining) {
if (!createClients)
return;
if (requestsRemaining <= 0)
return;
const req = http.get({
hostname: 'localhost',
pathname: '/',
port: server.address().port
}, cb).on('error', onerror);
count++;
onGC(req, { ongc });
setImmediate(getAll, requestsRemaining - 1);
}
function cb(res) {
res.resume();
done++;
}
function onerror(err) {
throw err;
}
function ongc() {
countGC++;
}
setImmediate(status);
function status() {
if (done > 0) {
createClients = false;
global.gc();
console.log(`done/collected/total: ${done}/${countGC}/${count}`);
if (countGC === count) {
server.close();
} else {
setImmediate(status);
}
} else {
setImmediate(status);
}
}

View File

@ -0,0 +1,65 @@
'use strict';
// Flags: --expose-gc
// Like test-gc-http-client.js, but with a timeout set.
const common = require('../common');
const { onGC } = require('../common/gc');
const http = require('http');
function serverHandler(req, res) {
setTimeout(function() {
req.resume();
res.writeHead(200);
res.end('hello\n');
}, 100);
}
const numRequests = 128;
let done = 0;
let countGC = 0;
const server = http.createServer(serverHandler);
server.listen(0, common.mustCall(() => {
getAll(numRequests);
}));
function getAll(requestsRemaining) {
if (requestsRemaining <= 0)
return;
const req = http.get({
hostname: 'localhost',
pathname: '/',
port: server.address().port
}, cb);
req.setTimeout(10, common.mustCall());
onGC(req, { ongc });
setImmediate(getAll, requestsRemaining - 1);
}
function cb(res) {
res.resume();
done += 1;
}
function ongc() {
countGC++;
}
setImmediate(status);
function status() {
if (done > 0) {
global.gc();
console.log(`done/collected/total: ${done}/${countGC}/${numRequests}`);
if (countGC === numRequests) {
server.close();
return;
}
}
setImmediate(status);
}

View File

@ -0,0 +1,71 @@
'use strict';
// Flags: --expose-gc
// just a simple http server and client.
const common = require('../common');
const { onGC } = require('../common/gc');
const cpus = require('os').availableParallelism();
function serverHandler(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}
const http = require('http');
const numRequests = 36;
let createClients = true;
let done = 0;
let count = 0;
let countGC = 0;
const server = http.createServer(serverHandler);
server.listen(0, common.mustCall(() => {
for (let i = 0; i < cpus; i++)
getAll(numRequests);
}));
function getAll(requestsRemaining) {
if (!createClients)
return;
if (requestsRemaining <= 0)
return;
const req = http.get({
hostname: 'localhost',
pathname: '/',
port: server.address().port
}, cb);
count++;
onGC(req, { ongc });
setImmediate(getAll, requestsRemaining - 1);
}
function cb(res) {
res.resume();
done += 1;
}
function ongc() {
countGC++;
}
setImmediate(status);
function status() {
if (done > 0) {
createClients = false;
global.gc();
console.log(`done/collected/total: ${done}/${countGC}/${count}`);
if (countGC === count) {
server.close();
} else {
setImmediate(status);
}
} else {
setImmediate(status);
}
}

View File

@ -0,0 +1,39 @@
'use strict';
// Flags: --expose-internals
require('../common');
const { getHeapSnapshotOptionTests, recordState } = require('../common/heap');
const tests = getHeapSnapshotOptionTests();
if (process.argv[2] === 'child') {
const { getHeapSnapshot } = require('v8');
require(tests.fixtures);
const { options, expected } = tests.cases[parseInt(process.argv[3])];
const snapshot = recordState(getHeapSnapshot(options));
console.log('Snapshot nodes', snapshot.snapshot.length);
console.log('Searching for', expected[0].children);
tests.check(snapshot, expected);
delete globalThis.obj; // To pass the leaked global tests.
return;
}
const { spawnSync } = require('child_process');
const assert = require('assert');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
for (let i = 0; i < tests.cases.length; ++i) {
const child = spawnSync(
process.execPath,
['--expose-internals', __filename, 'child', i + ''],
{
cwd: tmpdir.path
});
const stderr = child.stderr.toString();
const stdout = child.stdout.toString();
console.log('[STDERR]', stderr);
console.log('[STDOUT]', stdout);
assert.strictEqual(child.status, 0);
}

View File

@ -0,0 +1,50 @@
'use strict';
const common = require('../common');
if (common.isWindows)
common.skip('test not supported on Windows');
const assert = require('assert');
const validateHeapSnapshotFile = () => {
const fs = require('fs');
assert.strictEqual(process.listenerCount('SIGUSR2'), 1);
process.kill(process.pid, 'SIGUSR2');
process.kill(process.pid, 'SIGUSR2');
// Asynchronously wait for the snapshot. Use an async loop to be a bit more
// robust in case platform or machine differences throw off the timing.
(function validate() {
const files = fs.readdirSync(process.cwd());
if (files.length === 0)
return setImmediate(validate);
assert.strictEqual(files.length, 2);
for (let i = 0; i < files.length; i++) {
assert.match(files[i], /^Heap\..+\.heapsnapshot$/);
// Check the file is parsable as JSON
JSON.parse(fs.readFileSync(files[i]));
}
})();
};
if (process.argv[2] === 'child') {
validateHeapSnapshotFile();
} else {
// Modify the timezone. So we can check the file date string still returning correctly.
process.env.TZ = 'America/New_York';
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const args = ['--heapsnapshot-signal', 'SIGUSR2', '--diagnostic-dir', tmpdir.path, __filename, 'child'];
const child = spawnSync(process.execPath, args, { cwd: tmpdir.path });
assert.strictEqual(child.status, 0);
assert.strictEqual(child.signal, null);
}

View File

@ -0,0 +1,41 @@
'use strict';
const common = require('../common');
if (common.isWindows)
common.skip('test not supported on Windows');
const assert = require('assert');
if (process.argv[2] === 'child') {
const fs = require('fs');
assert.strictEqual(process.listenerCount('SIGUSR2'), 1);
process.kill(process.pid, 'SIGUSR2');
process.kill(process.pid, 'SIGUSR2');
// Asynchronously wait for the snapshot. Use an async loop to be a bit more
// robust in case platform or machine differences throw off the timing.
(function validate() {
const files = fs.readdirSync(process.cwd());
if (files.length === 0)
return setImmediate(validate);
assert.strictEqual(files.length, 2);
for (let i = 0; i < files.length; i++) {
assert.match(files[i], /^Heap\..+\.heapsnapshot$/);
JSON.parse(fs.readFileSync(files[i]));
}
})();
} else {
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const args = ['--heapsnapshot-signal', 'SIGUSR2', __filename, 'child'];
const child = spawnSync(process.execPath, args, { cwd: tmpdir.path });
assert.strictEqual(child.status, 0);
assert.strictEqual(child.signal, null);
}

View File

@ -0,0 +1,80 @@
'use strict';
const common = require('../common');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('process.chdir is not available in Workers');
}
const { writeHeapSnapshot, getHeapSnapshot } = require('v8');
const assert = require('assert');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
process.chdir(tmpdir.path);
{
writeHeapSnapshot('my.heapdump');
fs.accessSync('my.heapdump');
}
{
const heapdump = writeHeapSnapshot();
assert.strictEqual(typeof heapdump, 'string');
fs.accessSync(heapdump);
}
{
const directory = 'directory';
fs.mkdirSync(directory);
assert.throws(() => {
writeHeapSnapshot(directory);
}, (e) => {
assert.ok(e, 'writeHeapSnapshot should error');
assert.strictEqual(e.code, 'EISDIR');
assert.strictEqual(e.syscall, 'open');
return true;
});
}
[1, true, {}, [], null, Infinity, NaN].forEach((i) => {
assert.throws(() => writeHeapSnapshot(i), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "path" argument must be of type string or an instance of ' +
'Buffer or URL.' +
common.invalidArgTypeHelper(i)
});
});
[1, true, [], null, Infinity, NaN].forEach((i) => {
assert.throws(() => writeHeapSnapshot('test.heapsnapshot', i), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "options" argument must be of type object.' +
common.invalidArgTypeHelper(i)
});
});
[1, true, [], null, Infinity, NaN].forEach((i) => {
assert.throws(() => getHeapSnapshot(i), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "options" argument must be of type object.' +
common.invalidArgTypeHelper(i)
});
});
{
let data = '';
const snapshot = getHeapSnapshot();
snapshot.setEncoding('utf-8');
snapshot.on('data', common.mustCallAtLeast((chunk) => {
data += chunk.toString();
}));
snapshot.on('end', common.mustCall(() => {
JSON.parse(data);
}));
}

View File

@ -0,0 +1,157 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
// The test works by making a total of 8 requests to the server. The first
// two are made with the server off - they should come back as ECONNREFUSED.
// The next two are made with server on - they should come back successful.
// The next two are made with the server off - and so on. Without the fix
// we were experiencing parse errors instead of ECONNREFUSED.
// https://github.com/nodejs/node-v0.x-archive/issues/784
const http = require('http');
const assert = require('assert');
const server = http.createServer(function(req, res) {
let body = '';
req.setEncoding('utf8');
req.on('data', function(chunk) {
body += chunk;
});
req.on('end', function() {
assert.strictEqual(body, 'PING');
res.writeHead(200, { 'Connection': 'close' });
res.end('PONG');
});
});
server.on('listening', pingping);
function serverOn() {
console.error('Server ON');
server.listen(common.PORT);
}
function serverOff() {
console.error('Server OFF');
server.close();
pingping();
}
const responses = [];
function afterPing(result) {
responses.push(result);
console.error(`afterPing. responses.length = ${responses.length}`);
const ECONNREFUSED_RE = /ECONNREFUSED/;
const successRE = /success/;
switch (responses.length) {
case 2:
assert.match(responses[0], ECONNREFUSED_RE);
assert.match(responses[1], ECONNREFUSED_RE);
serverOn();
break;
case 4:
assert.match(responses[2], successRE);
assert.match(responses[3], successRE);
serverOff();
break;
case 6:
assert.match(responses[4], ECONNREFUSED_RE);
assert.match(responses[5], ECONNREFUSED_RE);
serverOn();
break;
case 8:
assert.match(responses[6], successRE);
assert.match(responses[7], successRE);
server.close();
// We should go to process.on('exit') from here.
break;
}
}
function ping() {
console.error('making req');
const opt = {
port: common.PORT,
path: '/ping',
method: 'POST'
};
const req = http.request(opt, function(res) {
let body = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
assert.strictEqual(body, 'PONG');
assert.ok(!hadError);
gotEnd = true;
afterPing('success');
});
});
req.end('PING');
let gotEnd = false;
let hadError = false;
req.on('error', function(error) {
console.log(`Error making ping req: ${error}`);
hadError = true;
assert.ok(!gotEnd);
// Family autoselection might be skipped if only a single address is returned by DNS.
const actualError = Array.isArray(error.errors) ? error.errors[0] : error;
afterPing(actualError.message);
});
}
function pingping() {
ping();
ping();
}
pingping();
process.on('exit', function() {
console.error("process.on('exit')");
console.error(responses);
assert.strictEqual(responses.length, 8);
});

View File

@ -0,0 +1,47 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
// This test assesses whether long-running writes can complete
// or timeout because the socket is not aware that the backing
// stream is still writing.
const writeSize = 3000000;
let socket;
const server = http.createServer(common.mustCall((req, res) => {
server.close();
const content = Buffer.alloc(writeSize, 0x44);
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Length': content.length.toString(),
'Vary': 'Accept-Encoding'
});
socket = res.socket;
const onTimeout = socket._onTimeout;
socket._onTimeout = common.mustCallAtLeast(() => onTimeout.call(socket), 1);
res.write(content);
res.end();
}));
server.on('timeout', () => {
// TODO(apapirovski): This test is faulty on certain Windows systems
// as no queue is ever created
assert(!socket._handle || socket._handle.writeQueueSize === 0,
'Should not timeout');
});
server.listen(0, common.mustCall(() => {
http.get({
path: '/',
port: server.address().port
}, (res) => {
res.once('data', () => {
socket._onTimeout();
res.on('data', () => {});
});
res.on('end', () => server.close());
});
}));

View File

@ -0,0 +1,107 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
const assert = require('assert');
const http = require('http');
const serverSockets = [];
const server = http.createServer(function(req, res) {
if (!serverSockets.includes(req.socket)) {
serverSockets.push(req.socket);
}
res.end(req.url);
});
server.listen(0, function() {
const agent = http.Agent({
keepAlive: true,
maxSockets: 5,
maxFreeSockets: 2
});
let closed = false;
makeReqs(10, function(er) {
assert.ifError(er);
assert.strictEqual(count(agent.freeSockets), 2);
assert.strictEqual(count(agent.sockets), 0);
assert.strictEqual(serverSockets.length, 5);
// Now make 10 more reqs.
// should use the 2 free reqs from the pool first.
makeReqs(10, function(er) {
assert.ifError(er);
assert.strictEqual(count(agent.freeSockets), 2);
assert.strictEqual(count(agent.sockets), 0);
assert.strictEqual(serverSockets.length, 8);
agent.destroy();
server.close(function() {
closed = true;
});
});
});
process.on('exit', function() {
assert(closed);
console.log('ok');
});
// make 10 requests in parallel,
// then 10 more when they all finish.
function makeReqs(n, cb) {
for (let i = 0; i < n; i++)
makeReq(i, then);
function then(er) {
if (er)
return cb(er);
else if (--n === 0)
setTimeout(cb, 100);
}
}
function makeReq(i, cb) {
http.request({
port: server.address().port,
path: `/${i}`,
agent: agent
}, function(res) {
let data = '';
res.setEncoding('ascii');
res.on('data', function(c) {
data += c;
});
res.on('end', function() {
assert.strictEqual(data, `/${i}`);
cb();
});
}).end();
}
function count(sockets) {
return Object.keys(sockets).reduce(function(n, name) {
return n + sockets[name].length;
}, 0);
}
});

View File

@ -0,0 +1,66 @@
// This test is designed to fail with a segmentation fault in Node.js 4.1.0 and
// execute without issues in Node.js 4.1.1 and up.
'use strict';
const common = require('../common');
const assert = require('assert');
const httpCommon = require('_http_common');
const { HTTPParser } = require('_http_common');
const net = require('net');
httpCommon.parsers.max = 50;
const COUNT = httpCommon.parsers.max + 1;
const parsers = new Array(COUNT);
for (let i = 0; i < parsers.length; i++)
parsers[i] = httpCommon.parsers.alloc();
let gotRequests = 0;
let gotResponses = 0;
function execAndClose() {
if (parsers.length === 0)
return;
process.stdout.write('.');
const parser = parsers.pop();
parser.initialize(HTTPParser.RESPONSE, {});
const socket = net.connect(common.PORT, common.localhostIPv4);
socket.on('error', (e) => {
// If SmartOS and ECONNREFUSED, then retry. See
// https://github.com/nodejs/node/issues/2663.
if (common.isSunOS && e.code === 'ECONNREFUSED') {
parsers.push(parser);
parser.reused = true;
socket.destroy();
setImmediate(execAndClose);
return;
}
throw e;
});
parser.consume(socket._handle);
parser.onIncoming = function onIncoming() {
process.stdout.write('+');
gotResponses++;
parser.unconsume();
httpCommon.freeParser(parser);
socket.destroy();
setImmediate(execAndClose);
};
}
const server = net.createServer(function(c) {
if (++gotRequests === COUNT)
server.close();
c.end('HTTP/1.1 200 OK\r\n\r\n', function() {
c.destroySoon();
});
}).listen(common.PORT, common.localhostIPv4, execAndClose);
process.on('exit', function() {
assert.strictEqual(gotResponses, COUNT);
});

View File

@ -0,0 +1,57 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const net = require('net');
const server = http.createServer(common.mustCall((req, res) => {
res.end();
}, 2));
server.keepAliveTimeout = common.platformTimeout(100);
server.listen(0, common.mustCall(() => {
const port = server.address().port;
const socket = net.connect({ port }, common.mustCall(() => {
request(common.mustCall(() => {
// Make a second request on the same socket, after the keep-alive timeout
// has been set on the server side.
request(common.mustCall());
}));
}));
server.on('timeout', common.mustCall(() => {
socket.end();
server.close();
}));
function request(callback) {
socket.setEncoding('utf8');
socket.on('data', onData);
let response = '';
// Simulate a client that sends headers slowly (with a period of inactivity
// that is longer than the keep-alive timeout).
socket.write('GET / HTTP/1.1\r\n' +
`Host: localhost:${port}\r\n`);
setTimeout(() => {
socket.write('Connection: keep-alive\r\n' +
'\r\n');
}, common.platformTimeout(300));
function onData(chunk) {
response += chunk;
if (chunk.includes('\r\n')) {
socket.removeListener('data', onData);
onHeaders();
}
}
function onHeaders() {
assert.ok(response.includes('HTTP/1.1 200 OK\r\n'));
assert.ok(response.includes('Connection: keep-alive\r\n'));
callback();
}
}
}));

View File

@ -0,0 +1,50 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const server = http.createServer(common.mustCall((req, res) => {
if (req.url === '/first') {
res.end('ok');
return;
}
setTimeout(() => {
res.end('ok');
}, common.platformTimeout(500));
}, 2));
server.keepAliveTimeout = common.platformTimeout(200);
const agent = new http.Agent({
keepAlive: true,
maxSockets: 1
});
function request(path, callback) {
const port = server.address().port;
const req = http.request({ agent, path, port }, common.mustCall((res) => {
assert.strictEqual(res.statusCode, 200);
res.setEncoding('utf8');
let result = '';
res.on('data', (chunk) => {
result += chunk;
});
res.on('end', common.mustCall(() => {
assert.strictEqual(result, 'ok');
callback();
}));
}));
req.end();
}
server.listen(0, common.mustCall(() => {
request('/first', () => {
request('/second', () => {
server.close();
});
});
}));

View File

@ -0,0 +1,133 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { createServer } = require('http');
const { connect } = require('net');
// This test validates that request are correct checked for both requests and headers timeout in various situations.
const requestBodyPart1 = 'POST / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 20\r\n';
const requestBodyPart2 = 'Connection: close\r\n\r\n1234567890';
const requestBodyPart3 = '1234567890';
const responseOk = 'HTTP/1.1 200 OK\r\n';
const responseTimeout = 'HTTP/1.1 408 Request Timeout\r\n';
const headersTimeout = common.platformTimeout(2000);
const connectionsCheckingInterval = headersTimeout / 8;
const server = createServer({
headersTimeout,
requestTimeout: headersTimeout * 2,
keepAliveTimeout: 0,
connectionsCheckingInterval
}, common.mustCall((req, res) => {
req.resume();
req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end();
});
}, 4));
assert.strictEqual(server.headersTimeout, headersTimeout);
assert.strictEqual(server.requestTimeout, headersTimeout * 2);
let i = 0;
function createClient(server) {
const request = {
index: i++,
client: connect(server.address().port),
response: '',
completed: false
};
request.client.setEncoding('utf8');
request.client.on('data', common.mustCallAtLeast((chunk) => {
request.response += chunk;
}));
request.client.on('end', common.mustCall(() => {
request.completed = true;
}));
request.client.on('error', common.mustNotCall());
request.client.resume();
return request;
}
server.listen(0, common.mustCall(() => {
const request1 = createClient(server);
let request2;
let request3;
let request4;
let request5;
// Send the first request and stop before the body
request1.client.write(requestBodyPart1);
// After a little while send two new requests
setTimeout(() => {
request2 = createClient(server);
request3 = createClient(server);
// Send the second request, stop in the middle of the headers
request2.client.write(requestBodyPart1);
// Send the third request and stop in the middle of the headers
request3.client.write(requestBodyPart1);
}, headersTimeout * 0.2);
// After another little while send the last two new requests
setTimeout(() => {
request4 = createClient(server);
request5 = createClient(server);
// Send the fourth request, stop in the middle of the headers
request4.client.write(requestBodyPart1);
// Send the fifth request, stop in the middle of the headers
request5.client.write(requestBodyPart1);
}, headersTimeout * 0.6);
setTimeout(() => {
// Finish the first request
request1.client.write(requestBodyPart2 + requestBodyPart3);
// Complete headers for all requests but second
request3.client.write(requestBodyPart2);
request4.client.write(requestBodyPart2);
request5.client.write(requestBodyPart2);
}, headersTimeout * 0.8);
setTimeout(() => {
// After the first timeout, the first request should have been completed and second timedout
assert(request1.completed);
assert(request2.completed);
assert(!request3.completed);
assert(!request4.completed);
assert(!request5.completed);
assert(request1.response.startsWith(responseOk));
assert(request2.response.startsWith(responseTimeout)); // It is expired due to headersTimeout
}, headersTimeout * 1.4);
setTimeout(() => {
// Complete the body for the fourth request
request4.client.write(requestBodyPart3);
}, headersTimeout * 1.5);
setTimeout(() => {
// All request should be completed now, either with 200 or 408
assert(request3.completed);
assert(request4.completed);
assert(request5.completed);
assert(request3.response.startsWith(responseTimeout)); // It is expired due to requestTimeout
assert(request4.response.startsWith(responseOk));
assert(request5.response.startsWith(responseTimeout)); // It is expired due to requestTimeout
server.close();
}, headersTimeout * 3 + connectionsCheckingInterval);
}));

View File

@ -0,0 +1,40 @@
'use strict';
// Test sending a large stream with a large initial window size.
// See: https://github.com/nodejs/node/issues/19141
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');
const server = http2.createServer({ settings: { initialWindowSize: 6553500 } });
server.on('stream', (stream) => {
stream.resume();
stream.respond();
stream.end('ok');
});
server.listen(0, common.mustCall(() => {
let remaining = 1e8;
const chunkLength = 1e6;
const chunk = Buffer.alloc(chunkLength, 'a');
const client = http2.connect(`http://localhost:${server.address().port}`,
{ settings: { initialWindowSize: 6553500 } });
const request = client.request({ ':method': 'POST' });
function writeChunk() {
if (remaining > 0) {
remaining -= chunkLength;
request.write(chunk, writeChunk);
} else {
request.end();
}
}
writeChunk();
request.on('close', common.mustCall(() => {
client.close();
server.close();
}));
request.resume();
}));

View File

@ -0,0 +1,48 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');
// Test that maxSessionMemory Caps work
const largeBuffer = Buffer.alloc(2e6);
const server = http2.createServer({ maxSessionMemory: 1 });
server.on('stream', common.mustCall((stream) => {
stream.on('error', (err) => {
if (err.code !== 'ECONNRESET')
throw err;
});
stream.respond();
stream.end(largeBuffer);
}));
server.listen(0, common.mustCall(() => {
const client = http2.connect(`http://localhost:${server.address().port}`);
{
const req = client.request();
req.on('response', () => {
// This one should be rejected because the server is over budget
// on the current memory allocation
const req = client.request();
req.on('error', common.expectsError({
code: 'ERR_HTTP2_STREAM_ERROR',
name: 'Error',
message: 'Stream closed with error code NGHTTP2_ENHANCE_YOUR_CALM'
}));
req.on('close', common.mustCall(() => {
server.close();
client.destroy();
}));
});
req.resume();
req.on('close', common.mustCall());
}
}));

View File

@ -0,0 +1,60 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const http2 = require('http2');
const net = require('net');
const http2util = require('../common/http2');
// Test that ping flooding causes the session to be torn down
const kSettings = new http2util.SettingsFrame();
const kPing = new http2util.PingFrame();
const server = http2.createServer();
let interval;
server.on('stream', common.mustNotCall());
server.on('session', common.mustCall((session) => {
session.on('error', (e) => {
assert.strictEqual(e.code, 'ERR_HTTP2_ERROR');
assert(e.message.includes('Flooding was detected'));
clearInterval(interval);
});
session.on('close', common.mustCall(() => {
server.close();
}));
}));
server.listen(0, common.mustCall(() => {
const client = net.connect(server.address().port);
// nghttp2 uses a limit of 10000 items in it's outbound queue.
// If this number is exceeded, a flooding error is raised.
// TODO(jasnell): Unfortunately, this test is inherently flaky because
// it is entirely dependent on how quickly the server is able to handle
// the inbound frames and whether those just happen to overflow nghttp2's
// outbound queue. The threshold at which the flood error occurs can vary
// from one system to another, and from one test run to another.
client.on('connect', common.mustCall(() => {
client.write(http2util.kClientMagic, () => {
client.write(kSettings.data, () => {
interval = setInterval(() => {
for (let n = 0; n < 10000; n++)
client.write(kPing.data);
}, 1);
});
});
}));
// An error event may or may not be emitted, depending on operating system
// and timing. We do not really care if one is emitted here or not, as the
// error on the server side is what we are testing for. Do not make this
// a common.mustCall() and there's no need to check the error details.
client.on('error', () => {});
}));

View File

@ -0,0 +1,58 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const http2 = require('http2');
const net = require('net');
const http2util = require('../common/http2');
// Test that settings flooding causes the session to be torn down
const kSettings = new http2util.SettingsFrame();
const server = http2.createServer();
let interval;
server.on('stream', common.mustNotCall());
server.on('session', common.mustCall((session) => {
session.on('error', (e) => {
assert.strictEqual(e.code, 'ERR_HTTP2_ERROR');
assert(e.message.includes('Flooding was detected'));
clearInterval(interval);
});
session.on('close', common.mustCall(() => {
server.close();
}));
}));
server.listen(0, common.mustCall(() => {
const client = net.connect(server.address().port);
// nghttp2 uses a limit of 10000 items in it's outbound queue.
// If this number is exceeded, a flooding error is raised.
// TODO(jasnell): Unfortunately, this test is inherently flaky because
// it is entirely dependent on how quickly the server is able to handle
// the inbound frames and whether those just happen to overflow nghttp2's
// outbound queue. The threshold at which the flood error occurs can vary
// from one system to another, and from one test run to another.
client.on('connect', common.mustCall(() => {
client.write(http2util.kClientMagic, () => {
interval = setInterval(() => {
for (let n = 0; n < 10000; n++)
client.write(kSettings.data);
}, 1);
});
}));
// An error event may or may not be emitted, depending on operating system
// and timing. We do not really care if one is emitted here or not, as the
// error on the server side is what we are testing for. Do not make this
// a common.mustCall() and there's no need to check the error details.
client.on('error', () => {});
}));

View File

@ -0,0 +1,86 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const fs = require('fs');
const http2 = require('http2');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
// This test assesses whether long-running writes can complete
// or timeout because the session or stream are not aware that the
// backing stream is still writing.
// To simulate a slow client, we write a really large chunk and
// then proceed through the following cycle:
// 1) Receive first 'data' event and record currently written size
// 2) Once we've read up to currently written size recorded above,
// we pause the stream and wait longer than the server timeout
// 3) Socket.prototype._onTimeout triggers and should confirm
// that the backing stream is still active and writing
// 4) Our timer fires, we resume the socket and start at 1)
const writeSize = 3000000;
const minReadSize = 500000;
const serverTimeout = common.platformTimeout(500);
let offsetTimeout = common.platformTimeout(100);
let didReceiveData = false;
const content = Buffer.alloc(writeSize, 0x44);
const filepath = tmpdir.resolve('http2-large-write.tmp');
fs.writeFileSync(filepath, content, 'binary');
const fd = fs.openSync(filepath, 'r');
process.on('beforeExit', () => fs.closeSync(fd));
const server = http2.createSecureServer({
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem')
});
server.on('stream', common.mustCall((stream) => {
stream.respondWithFD(fd, {
'Content-Type': 'application/octet-stream',
'Content-Length': content.length.toString(),
'Vary': 'Accept-Encoding'
});
stream.end();
}));
server.setTimeout(serverTimeout);
server.on('timeout', () => {
assert.ok(!didReceiveData, 'Should not timeout');
});
server.listen(0, common.mustCall(() => {
const client = http2.connect(`https://localhost:${server.address().port}`,
{ rejectUnauthorized: false });
const req = client.request({ ':path': '/' });
req.end();
const resume = () => req.resume();
let receivedBufferLength = 0;
let firstReceivedAt;
req.on('data', common.mustCallAtLeast((buf) => {
if (receivedBufferLength === 0) {
didReceiveData = false;
firstReceivedAt = Date.now();
}
receivedBufferLength += buf.length;
if (receivedBufferLength >= minReadSize &&
receivedBufferLength < writeSize) {
didReceiveData = true;
receivedBufferLength = 0;
req.pause();
setTimeout(
resume,
serverTimeout + offsetTimeout - (Date.now() - firstReceivedAt)
);
offsetTimeout = 0;
}
}, 1));
req.on('end', common.mustCall(() => {
client.close();
server.close();
}));
}));

View File

@ -0,0 +1,84 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const fixtures = require('../common/fixtures');
const http2 = require('http2');
// This test assesses whether long-running writes can complete
// or timeout because the session or stream are not aware that the
// backing stream is still writing.
// To simulate a slow client, we write a really large chunk and
// then proceed through the following cycle:
// 1) Receive first 'data' event and record currently written size
// 2) Once we've read up to currently written size recorded above,
// we pause the stream and wait longer than the server timeout
// 3) Socket.prototype._onTimeout triggers and should confirm
// that the backing stream is still active and writing
// 4) Our timer fires, we resume the socket and start at 1)
const writeSize = 3000000;
const minReadSize = 500000;
const serverTimeout = common.platformTimeout(500);
let offsetTimeout = common.platformTimeout(100);
let didReceiveData = false;
const server = http2.createSecureServer({
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem')
});
server.on('stream', common.mustCall((stream) => {
const content = Buffer.alloc(writeSize, 0x44);
stream.respond({
'Content-Type': 'application/octet-stream',
'Content-Length': content.length.toString(),
'Vary': 'Accept-Encoding'
});
stream.write(content);
stream.setTimeout(serverTimeout);
stream.on('timeout', () => {
assert.ok(!didReceiveData, 'Should not timeout');
});
stream.end();
}));
server.setTimeout(serverTimeout);
server.on('timeout', () => {
assert.ok(!didReceiveData, 'Should not timeout');
});
server.listen(0, common.mustCall(() => {
const client = http2.connect(`https://localhost:${server.address().port}`,
{ rejectUnauthorized: false });
const req = client.request({ ':path': '/' });
req.end();
const resume = () => req.resume();
let receivedBufferLength = 0;
let firstReceivedAt;
req.on('data', common.mustCallAtLeast((buf) => {
if (receivedBufferLength === 0) {
didReceiveData = false;
firstReceivedAt = Date.now();
}
receivedBufferLength += buf.length;
if (receivedBufferLength >= minReadSize &&
receivedBufferLength < writeSize) {
didReceiveData = true;
receivedBufferLength = 0;
req.pause();
setTimeout(
resume,
serverTimeout + offsetTimeout - (Date.now() - firstReceivedAt)
);
offsetTimeout = 0;
}
}, 1));
req.on('end', common.mustCall(() => {
client.close();
server.close();
}));
}));

View File

@ -0,0 +1,34 @@
'use strict';
const common = require('../common');
const fixtures = require('../common/fixtures');
if (!common.hasCrypto)
common.skip('missing crypto');
const https = require('https');
const assert = require('assert');
{
const server = https.createServer({
cert: fixtures.readKey('agent1-cert.pem'),
key: fixtures.readKey('agent1-key.pem'),
}, common.mustCall((req, res) => {
server.close();
res.end();
}));
server.listen(0, '127.0.0.1', common.mustCall(() => {
const port = server.address().port;
const req = https.get({
host: 'localhost',
pathname: '/',
port,
family: 4,
localPort: common.PORT,
rejectUnauthorized: false,
}, common.mustCall(() => {
assert.strictEqual(req.socket.localPort, common.PORT);
assert.strictEqual(req.socket.remotePort, port);
}));
}));
}

View File

@ -0,0 +1,79 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const fixtures = require('../common/fixtures');
const https = require('https');
const tls = require('tls');
const tests = [];
const serverOptions = {
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem')
};
function test(fn) {
if (!tests.length) {
process.nextTick(run);
}
tests.push(fn);
}
function run() {
const fn = tests.shift();
if (fn) fn(run);
}
test(function serverKeepAliveTimeoutWithPipeline(cb) {
const server = https.createServer(
serverOptions,
common.mustCall((req, res) => {
res.end();
}, 3));
server.setTimeout(common.platformTimeout(500), common.mustCall((socket) => {
// End this test and call `run()` for the next test (if any).
socket.destroy();
server.close();
cb();
}));
server.keepAliveTimeout = common.platformTimeout(50);
server.listen(0, common.mustCall(() => {
const options = {
port: server.address().port,
allowHalfOpen: true,
rejectUnauthorized: false
};
const c = tls.connect(options, () => {
c.write('GET /1 HTTP/1.1\r\nHost: localhost\r\n\r\n');
c.write('GET /2 HTTP/1.1\r\nHost: localhost\r\n\r\n');
c.write('GET /3 HTTP/1.1\r\nHost: localhost\r\n\r\n');
});
}));
});
test(function serverNoEndKeepAliveTimeoutWithPipeline(cb) {
const server = https.createServer(serverOptions, common.mustCall(3));
server.setTimeout(common.platformTimeout(500), common.mustCall((socket) => {
// End this test and call `run()` for the next test (if any).
socket.destroy();
server.close();
cb();
}));
server.keepAliveTimeout = common.platformTimeout(50);
server.listen(0, common.mustCall(() => {
const options = {
port: server.address().port,
allowHalfOpen: true,
rejectUnauthorized: false
};
const c = tls.connect(options, () => {
c.write('GET /1 HTTP/1.1\r\nHost: localhost\r\n\r\n');
c.write('GET /2 HTTP/1.1\r\nHost: localhost\r\n\r\n');
c.write('GET /3 HTTP/1.1\r\nHost: localhost\r\n\r\n');
});
}));
});

View File

@ -0,0 +1,65 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
const child = require('child_process');
const fixtures = require('../common/fixtures');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('process.chdir is not available in Workers');
}
if (process.env.TEST_INIT) {
return process.stdout.write('Loaded successfully!');
}
process.env.TEST_INIT = 1;
function test(file, expected) {
child.exec(...common.escapePOSIXShell`"${process.execPath}" "${file}"`, common.mustSucceed((out) => {
assert.strictEqual(out, expected, `'node ${file}' failed!`);
}));
}
{
// Change CWD as we do this test so it's not dependent on current CWD
// being in the test folder
process.chdir(__dirname);
test('test-init', 'Loaded successfully!');
test('test-init.js', 'Loaded successfully!');
}
{
// test-init-index is in fixtures dir as requested by ry, so go there
process.chdir(fixtures.path());
test('test-init-index', 'Loaded successfully!');
}
{
// Ensures that `node fs` does not mistakenly load the native 'fs' module
// instead of the desired file and that the fs module loads as
// expected in node
process.chdir(fixtures.path('test-init-native'));
test('fs', 'fs loaded successfully');
}

View File

@ -0,0 +1,76 @@
import * as common from '../common/index.mjs';
import assert from 'node:assert';
import net from 'node:net';
import url from 'node:url';
import { fork } from 'node:child_process';
common.skipIfInspectorDisabled();
if (process.env.BE_CHILD) {
await beChild();
} else {
let firstPort;
const filename = url.fileURLToPath(import.meta.url);
const child = fork(filename, { env: { ...process.env, BE_CHILD: 1 } });
child.once('message', common.mustCall((msg) => {
assert.strictEqual(msg.cmd, 'started');
assert.strictEqual(msg.url, undefined);
child.send({ cmd: 'open' });
child.once('message', common.mustCall(wasOpenedHandler));
}));
function wasOpenedHandler(msg) {
assert.strictEqual(msg.cmd, 'url');
const { port } = url.parse(msg.url);
ping(port, common.mustSucceed(() => {
child.send({ cmd: 'dispose' });
child.once('message', common.mustCall(wasDisposedWhenOpenHandler));
firstPort = port;
}));
}
function wasDisposedWhenOpenHandler(msg) {
assert.strictEqual(msg.cmd, 'url');
assert.strictEqual(msg.url, undefined);
ping(firstPort, (err) => {
assert(err, 'expected ping to inspector port to fail');
child.send({ cmd: 'dispose' });
child.once('message', common.mustCall(wasReDisposedHandler));
});
}
function wasReDisposedHandler(msg) {
assert.strictEqual(msg.cmd, 'url');
assert.strictEqual(msg.url, undefined);
process.exit();
}
}
function ping(port, callback) {
net.connect({ port, family: 4 })
.on('connect', function() { close(this); })
.on('error', function(err) { close(this, err); });
function close(self, err) {
self.end();
self.on('close', () => callback(err));
}
}
async function beChild() {
const inspector = await import('node:inspector');
let inspectorDisposer;
process.send({ cmd: 'started' });
process.on('message', (msg) => {
if (msg.cmd === 'open') {
inspectorDisposer = inspector.open(0, false, undefined);
}
if (msg.cmd === 'dispose') {
inspectorDisposer[Symbol.dispose]();
}
process.send({ cmd: 'url', url: inspector.url() });
});
}

View File

@ -0,0 +1,353 @@
'use strict';
// Flags: --expose-internals
const common = require('../common');
common.skipIfInspectorDisabled();
const assert = require('assert');
const cluster = require('cluster');
const debuggerPort = common.PORT;
const childProcess = require('child_process');
let offset = 0;
// This test suite checks that inspector port in cluster is incremented
// for different execArgv combinations
function testRunnerMain() {
let defaultPortCase = spawnPrimary({
execArgv: ['--inspect'],
workers: [{ expectedPort: 9230 }]
});
spawnPrimary({
execArgv: ['--inspect=65534'],
workers: [
{ expectedPort: 65535 },
{ expectedPort: 1024 },
{ expectedPort: 1025 },
{ expectedPort: 1026 },
]
});
let port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
workers: [
{ expectedPort: port + 1 },
{ expectedPort: port + 2 },
{ expectedPort: port + 3 },
]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: ['--inspect', `--inspect-port=${port}`],
workers: [{ expectedPort: port + 1 }]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: ['--inspect', `--debug-port=${port}`],
workers: [{ expectedPort: port + 1 }]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=0.0.0.0:${port}`],
workers: [{ expectedPort: port + 1, expectedHost: '0.0.0.0' }]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=127.0.0.1:${port}`],
workers: [{ expectedPort: port + 1, expectedHost: '127.0.0.1' }]
});
if (common.hasIPv6) {
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=[::]:${port}`],
workers: [{ expectedPort: port + 1, expectedHost: '::' }]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=[::1]:${port}`],
workers: [{ expectedPort: port + 1, expectedHost: '::1' }]
});
}
// These tests check that setting inspectPort in cluster.settings
// would take effect and override port incrementing behavior
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: port + 2 },
workers: [{ expectedPort: port + 2 }]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 'addTwo' },
workers: [
{ expectedPort: port + 2 },
{ expectedPort: port + 4 },
]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 'string' },
workers: [{}]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 'null' },
workers: [{}]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 'bignumber' },
workers: [{}]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 'negativenumber' },
workers: [{}]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 'bignumberfunc' },
workers: [{}]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 'strfunc' },
workers: [{}]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [],
clusterSettings: { inspectPort: port, execArgv: ['--inspect'] },
workers: [
{ expectedPort: port },
]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [`--inspect=${port}`],
clusterSettings: { inspectPort: 0 },
workers: [
{ expectedInitialPort: 0 },
{ expectedInitialPort: 0 },
{ expectedInitialPort: 0 },
]
});
port = debuggerPort + offset++ * 5;
spawnPrimary({
execArgv: [],
clusterSettings: { inspectPort: 0 },
workers: [
{ expectedInitialPort: 0 },
{ expectedInitialPort: 0 },
{ expectedInitialPort: 0 },
]
});
defaultPortCase.then(() => {
port = debuggerPort + offset++ * 5;
defaultPortCase = spawnPrimary({
execArgv: ['--inspect'],
clusterSettings: { inspectPort: port + 2 },
workers: [
{ expectedInitialPort: port + 2 },
]
});
});
}
function primaryProcessMain() {
const workers = JSON.parse(process.env.workers);
const clusterSettings = JSON.parse(process.env.clusterSettings) || {};
const badPortError = { name: 'RangeError', code: 'ERR_SOCKET_BAD_PORT' };
let debugPort = process.debugPort;
for (const worker of workers) {
const params = {};
if (worker.expectedPort) {
params.expectedPort = worker.expectedPort;
}
if (worker.expectedInitialPort) {
params.expectedInitialPort = worker.expectedInitialPort;
}
if (worker.expectedHost) {
params.expectedHost = worker.expectedHost;
}
clusterSettings.execArgv = clusterSettings.execArgv ?
clusterSettings.execArgv.concat(['--expose-internals']) :
process.execArgv.concat(['--expose-internals']);
if (clusterSettings.inspectPort === 'addTwo') {
clusterSettings.inspectPort = common.mustCall(
() => { return debugPort += 2; },
workers.length
);
} else if (clusterSettings.inspectPort === 'string') {
clusterSettings.inspectPort = 'string';
cluster.setupPrimary(clusterSettings);
assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, badPortError);
return;
} else if (clusterSettings.inspectPort === 'null') {
clusterSettings.inspectPort = null;
cluster.setupPrimary(clusterSettings);
assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, badPortError);
return;
} else if (clusterSettings.inspectPort === 'bignumber') {
clusterSettings.inspectPort = 1293812;
cluster.setupPrimary(clusterSettings);
assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, badPortError);
return;
} else if (clusterSettings.inspectPort === 'negativenumber') {
clusterSettings.inspectPort = -9776;
cluster.setupPrimary(clusterSettings);
assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, badPortError);
return;
} else if (clusterSettings.inspectPort === 'bignumberfunc') {
clusterSettings.inspectPort = common.mustCall(
() => 123121,
workers.length
);
cluster.setupPrimary(clusterSettings);
assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, badPortError);
return;
} else if (clusterSettings.inspectPort === 'strfunc') {
clusterSettings.inspectPort = common.mustCall(
() => 'invalidPort',
workers.length
);
cluster.setupPrimary(clusterSettings);
assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, badPortError);
return;
}
cluster.setupPrimary(clusterSettings);
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}
}
function workerProcessMain() {
const { expectedPort, expectedInitialPort, expectedHost } = process.env;
const debugOptions =
require('internal/options').getOptionValue('--inspect-port');
if ('expectedPort' in process.env) {
assert.strictEqual(process.debugPort, +expectedPort);
}
if ('expectedInitialPort' in process.env) {
assert.strictEqual(debugOptions.port, +expectedInitialPort);
}
if ('expectedHost' in process.env) {
assert.strictEqual(debugOptions.host, expectedHost);
}
process.exit();
}
function spawnPrimary({ execArgv, workers, clusterSettings = {} }) {
return new Promise((resolve) => {
childProcess.fork(__filename, {
env: { ...process.env,
workers: JSON.stringify(workers),
clusterSettings: JSON.stringify(clusterSettings),
testProcess: true },
execArgv: execArgv.concat(['--expose-internals'])
}).on('exit', common.mustCall((code, signal) => {
checkExitCode(code, signal);
resolve();
}));
});
}
function checkExitCode(code, signal) {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}
if (!process.env.testProcess) {
testRunnerMain();
} else if (cluster.isPrimary) {
primaryProcessMain();
} else {
workerProcessMain();
}

View File

@ -0,0 +1,373 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const backslash = /\\/g;
process.on('warning', common.mustCall());
console.error('load test-module-loading.js');
assert.strictEqual(require.main.id, '.');
assert.strictEqual(require.main, module);
assert.strictEqual(process.mainModule, module);
// Assert that it's *not* the main module in the required module.
require('../fixtures/not-main-module.js');
{
// Require a file with a request that includes the extension
const a_js = require('../fixtures/a.js');
assert.strictEqual(a_js.number, 42);
}
{
// Require a file without any extensions
const foo_no_ext = require('../fixtures/foo');
assert.strictEqual(foo_no_ext.foo, 'ok');
}
const a = require('../fixtures/a');
const d = require('../fixtures/b/d');
const d2 = require('../fixtures/b/d');
{
const c = require('../fixtures/b/c');
// Absolute
const d3 = require(path.join(__dirname, '../fixtures/b/d'));
// Relative
const d4 = require('../fixtures/b/d');
assert.ok(a.A instanceof Function);
assert.strictEqual(a.A(), 'A');
assert.ok(a.C instanceof Function);
assert.strictEqual(a.C(), 'C');
assert.ok(a.D instanceof Function);
assert.strictEqual(a.D(), 'D');
assert.ok(d.D instanceof Function);
assert.strictEqual(d.D(), 'D');
assert.ok(d2.D instanceof Function);
assert.strictEqual(d2.D(), 'D');
assert.ok(d3.D instanceof Function);
assert.strictEqual(d3.D(), 'D');
assert.ok(d4.D instanceof Function);
assert.strictEqual(d4.D(), 'D');
assert.ok((new a.SomeClass()) instanceof c.SomeClass);
}
{
console.error('test index.js modules ids and relative loading');
const one = require('../fixtures/nested-index/one');
const two = require('../fixtures/nested-index/two');
assert.notStrictEqual(one.hello, two.hello);
}
{
console.error('test index.js in a folder with a trailing slash');
const three = require('../fixtures/nested-index/three');
const threeFolder = require('../fixtures/nested-index/three/');
const threeIndex = require('../fixtures/nested-index/three/index.js');
assert.strictEqual(threeFolder, threeIndex);
assert.notStrictEqual(threeFolder, three);
}
assert.strictEqual(require('../fixtures/packages/index').ok, 'ok');
assert.strictEqual(require('../fixtures/packages/main').ok, 'ok');
assert.strictEqual(require('../fixtures/packages/main-index').ok, 'ok');
common.expectWarning(
'DeprecationWarning',
"Invalid 'main' field in '" +
path.toNamespacedPath(require.resolve('../fixtures/packages/missing-main/package.json')) +
"' of 'doesnotexist.js'. Please either fix that or report it to the" +
' module author',
'DEP0128');
assert.strictEqual(require('../fixtures/packages/missing-main').ok, 'ok');
assert.throws(
() => require('../fixtures/packages/missing-main-no-index'),
{
code: 'MODULE_NOT_FOUND',
message: /packages[/\\]missing-main-no-index[/\\]doesnotexist\.js'\. Please.+package\.json.+valid "main"/,
path: /fixtures[/\\]packages[/\\]missing-main-no-index[/\\]package\.json/,
requestPath: /^\.\.[/\\]fixtures[/\\]packages[/\\]missing-main-no-index$/
}
);
assert.throws(
function() { require('../fixtures/packages/unparseable'); },
{ code: 'ERR_INVALID_PACKAGE_CONFIG' }
);
{
console.error('test cycles containing a .. path');
const root = require('../fixtures/cycles/root');
const foo = require('../fixtures/cycles/folder/foo');
assert.strictEqual(root.foo, foo);
assert.strictEqual(root.sayHello(), root.hello);
}
console.error('test node_modules folders');
// Asserts are in the fixtures files themselves,
// since they depend on the folder structure.
require('../fixtures/node_modules/foo');
{
console.error('test name clashes');
// This one exists and should import the local module
const my_path = require('../fixtures/path');
assert.ok(my_path.path_func instanceof Function);
// This one does not exist and should throw
assert.throws(function() { require('./utils'); },
/^Error: Cannot find module '\.\/utils'/);
}
let errorThrown = false;
try {
require('../fixtures/throws_error');
} catch (e) {
errorThrown = true;
assert.strictEqual(e.message, 'blah');
}
assert.strictEqual(path.dirname(__filename), __dirname);
console.error('load custom file types with extensions');
require.extensions['.test'] = function(module, filename) {
let content = fs.readFileSync(filename).toString();
assert.strictEqual(content, 'this is custom source\n');
content = content.replace('this is custom source',
'exports.test = \'passed\'');
module._compile(content, filename);
};
assert.strictEqual(require('../fixtures/registerExt').test, 'passed');
// Unknown extension, load as .js
assert.strictEqual(require('../fixtures/registerExt.hello.world').test,
'passed');
console.error('load custom file types that return non-strings');
require.extensions['.test'] = function(module) {
module.exports = {
custom: 'passed'
};
};
assert.strictEqual(require('../fixtures/registerExt2').custom, 'passed');
assert.strictEqual(require('../fixtures/foo').foo, 'ok');
// Should not attempt to load a directory
assert.throws(
() => {
tmpdir.refresh();
require(tmpdir.path);
},
(err) => err.message.startsWith(`Cannot find module '${tmpdir.path}`)
);
{
// Check load order is as expected
console.error('load order');
const loadOrder = '../fixtures/module-load-order/';
require.extensions['.reg'] = require.extensions['.js'];
require.extensions['.reg2'] = require.extensions['.js'];
assert.strictEqual(require(`${loadOrder}file1`).file1, 'file1');
assert.strictEqual(require(`${loadOrder}file2`).file2, 'file2.js');
assert.throws(
() => require(`${loadOrder}file3`),
(e) => {
// Not a real .node module, but we know we require'd the right thing.
if (common.isOpenBSD) { // OpenBSD errors with non-ELF object error
assert.match(e.message, /File not an ELF object/);
} else {
assert.match(e.message, /file3\.node/);
}
return true;
}
);
assert.strictEqual(require(`${loadOrder}file4`).file4, 'file4.reg');
assert.strictEqual(require(`${loadOrder}file5`).file5, 'file5.reg2');
assert.strictEqual(require(`${loadOrder}file6`).file6, 'file6/index.js');
assert.throws(
() => require(`${loadOrder}file7`),
(e) => {
if (common.isOpenBSD) {
assert.match(e.message, /File not an ELF object/);
} else {
assert.match(e.message.replace(backslash, '/'), /file7\/index\.node/);
}
return true;
}
);
assert.strictEqual(require(`${loadOrder}file8`).file8, 'file8/index.reg');
assert.strictEqual(require(`${loadOrder}file9`).file9, 'file9/index.reg2');
}
{
// Make sure that module.require() is the same as
// doing require() inside of that module.
const parent = require('../fixtures/module-require/parent/');
const child = require('../fixtures/module-require/child/');
assert.strictEqual(child.loaded, parent.loaded);
}
{
// Loading JSON files with require()
// See https://github.com/nodejs/node-v0.x-archive/issues/1357.
const json = require('../fixtures/packages/main/package.json');
assert.deepStrictEqual(json, {
name: 'package-name',
version: '1.2.3',
main: 'package-main-module'
});
}
{
// Now verify that module.children contains all the different
// modules that we've required, and that all of them contain
// the appropriate children, and so on.
const visited = new Set();
const children = module.children.reduce(function red(set, child) {
if (visited.has(child)) return set;
visited.add(child);
let id = path.relative(path.dirname(__dirname), child.id);
id = id.replace(backslash, '/');
set[id] = child.children.reduce(red, {});
return set;
}, {});
assert.deepStrictEqual(children, {
'common/index.js': {
'common/tmpdir.js': {}
},
'fixtures/not-main-module.js': {},
'fixtures/a.js': {
'fixtures/b/c.js': {
'fixtures/b/d.js': {},
'fixtures/b/package/index.js': {}
}
},
'fixtures/foo': {},
'fixtures/nested-index/one/index.js': {
'fixtures/nested-index/one/hello.js': {}
},
'fixtures/nested-index/two/index.js': {
'fixtures/nested-index/two/hello.js': {}
},
'fixtures/nested-index/three.js': {},
'fixtures/nested-index/three/index.js': {},
'fixtures/packages/index/index.js': {},
'fixtures/packages/main/package-main-module.js': {},
'fixtures/packages/main-index/package-main-module/index.js': {},
'fixtures/packages/missing-main/index.js': {},
'fixtures/cycles/root.js': {
'fixtures/cycles/folder/foo.js': {}
},
'fixtures/node_modules/foo.js': {
'fixtures/node_modules/baz/index.js': {
'fixtures/node_modules/bar.js': {},
'fixtures/node_modules/baz/node_modules/asdf.js': {}
}
},
'fixtures/path.js': {},
'fixtures/registerExt.test': {},
'fixtures/registerExt.hello.world': {},
'fixtures/registerExt2.test': {},
'fixtures/module-load-order/file1': {},
'fixtures/module-load-order/file2.js': {},
'fixtures/module-load-order/file4.reg': {},
'fixtures/module-load-order/file5.reg2': {},
'fixtures/module-load-order/file6/index.js': {},
'fixtures/module-load-order/file8/index.reg': {},
'fixtures/module-load-order/file9/index.reg2': {},
'fixtures/module-require/parent/index.js': {
'fixtures/module-require/child/index.js': {
'fixtures/module-require/child/node_modules/target.js': {}
}
},
'fixtures/packages/main/package.json': {}
});
}
process.on('exit', function() {
assert.ok(a.A instanceof Function);
assert.strictEqual(a.A(), 'A done');
assert.ok(a.C instanceof Function);
assert.strictEqual(a.C(), 'C done');
assert.ok(a.D instanceof Function);
assert.strictEqual(a.D(), 'D done');
assert.ok(d.D instanceof Function);
assert.strictEqual(d.D(), 'D done');
assert.ok(d2.D instanceof Function);
assert.strictEqual(d2.D(), 'D done');
assert.strictEqual(errorThrown, true);
console.log('exit');
});
// Loading files with a byte order marker.
// See https://github.com/nodejs/node-v0.x-archive/issues/1440.
assert.strictEqual(require('../fixtures/utf8-bom.js'), 42);
assert.strictEqual(require('../fixtures/utf8-bom.json'), 42);
// Loading files with BOM + shebang.
// See https://github.com/nodejs/node/issues/27767
assert.throws(() => {
require('../fixtures/utf8-bom-shebang-shebang.js');
}, { name: 'SyntaxError' });
assert.strictEqual(require('../fixtures/utf8-shebang-bom.js'), 42);
// Error on the first line of a module should
// have the correct line number
assert.throws(function() {
require('../fixtures/test-error-first-line-offset.js');
}, function(err) {
return /test-error-first-line-offset\.js:1:/.test(err.stack);
}, 'Expected appearance of proper offset in Error stack');

View File

@ -0,0 +1,99 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
// Ref: https://github.com/nodejs/node-v0.x-archive/issues/5504
//
// This test only fails with CentOS 6.3 using kernel version 2.6.32.
// On other Linuxes and macOS, the `read` call gets an ECONNRESET in
// that case. On sunos, the `write` call fails with EPIPE.
//
// However, old CentOS will occasionally send an EOF instead of an
// ECONNRESET or EPIPE when the client has been destroyed abruptly.
//
// Make sure we don't keep trying to write or read more in that case.
switch (process.argv[2]) {
case 'server': return server();
case 'client': return client();
case undefined: return parent();
default: throw new Error('invalid');
}
function server() {
const net = require('net');
const content = Buffer.alloc(64 * 1024 * 1024, '#');
net.createServer(function(socket) {
this.close();
socket.on('end', function() {
console.error('end');
});
socket.on('_socketEnd', function() {
console.error('_socketEnd');
});
socket.write(content);
}).listen(common.PORT, common.localhostIPv4, function() {
console.log('listening');
});
}
function client() {
const net = require('net');
const client = net.connect({
host: common.localhostIPv4,
port: common.PORT
}, function() {
client.destroy();
});
}
function parent() {
const spawn = require('child_process').spawn;
const node = process.execPath;
const s = spawn(node, [__filename, 'server'], {
env: Object.assign(process.env, {
NODE_DEBUG: 'net'
})
});
wrap(s.stderr, process.stderr, 'SERVER 2>');
wrap(s.stdout, process.stdout, 'SERVER 1>');
s.on('exit', common.mustCall());
s.stdout.once('data', common.mustCall(function() {
const c = spawn(node, [__filename, 'client']);
wrap(c.stderr, process.stderr, 'CLIENT 2>');
wrap(c.stdout, process.stdout, 'CLIENT 1>');
c.on('exit', common.mustCall());
}));
function wrap(inp, out, w) {
inp.setEncoding('utf8');
inp.on('data', function(chunk) {
chunk = chunk.trim();
if (!chunk) return;
out.write(`${w}${chunk.split('\n').join(`\n${w}`)}\n`);
});
}
}

View File

@ -0,0 +1,17 @@
'use strict';
const common = require('../common');
const net = require('net');
const assert = require('assert');
const c = net.createConnection(common.PORT);
c.on('connect', common.mustNotCall());
c.on('error', common.mustCall(function(error) {
// Family autoselection might be skipped if only a single address is returned by DNS.
const failedAttempt = Array.isArray(error.errors) ? error.errors[0] : error;
assert.strictEqual(failedAttempt.code, 'ECONNREFUSED');
assert.strictEqual(failedAttempt.port, common.PORT);
assert.match(failedAttempt.address, /^(127\.0\.0\.1|::1)$/);
}));

View File

@ -0,0 +1,68 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
// Verify that connect reqs are properly cleaned up.
const common = require('../common');
const assert = require('assert');
const net = require('net');
const ROUNDS = 5;
const ATTEMPTS_PER_ROUND = 50;
let rounds = 1;
let reqs = 0;
let port;
const server = net.createServer().listen(0, common.mustCall(() => {
port = server.address().port;
server.close(common.mustCall(pummel));
}));
function pummel() {
let pending;
for (pending = 0; pending < ATTEMPTS_PER_ROUND; pending++) {
net.createConnection({ port, autoSelectFamily: false }).on('error', function(error) {
// Family autoselection might be skipped if only a single address is returned by DNS.
const actualError = Array.isArray(error.errors) ? error.errors[0] : error;
console.log('pending', pending, 'rounds', rounds);
assert.strictEqual(actualError.code, 'ECONNREFUSED');
if (--pending > 0) return;
if (rounds === ROUNDS) return check();
rounds++;
pummel();
});
reqs++;
}
}
function check() {
setTimeout(common.mustCall(function() {
assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'TCPWRAP').length, 0);
}), 0);
}
process.on('exit', function() {
assert.strictEqual(rounds, ROUNDS);
assert.strictEqual(reqs, ROUNDS * ATTEMPTS_PER_ROUND);
});

View File

@ -0,0 +1,32 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const net = require('net');
const assert = require('assert');
const c = net.createConnection(common.PORT);
c.on('connect', common.mustNotCall());
c.on('error', common.mustCall((e) => {
assert.strictEqual(c.connecting, false);
assert.strictEqual(e.code, 'ECONNREFUSED');
}));

View File

@ -0,0 +1,46 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
// EADDRINUSE is expected to occur on FreeBSD
// Ref: https://github.com/nodejs/node/issues/13055
const expectedErrorCodes = ['ECONNREFUSED', 'EADDRINUSE'];
const optionsIPv4 = {
port: common.PORT,
family: 4,
localPort: common.PORT + 1,
localAddress: common.localhostIPv4
};
const optionsIPv6 = {
host: '::1',
family: 6,
port: common.PORT + 2,
localPort: common.PORT + 3,
localAddress: '::1',
};
function onError(err, options) {
assert.ok(expectedErrorCodes.includes(err.code));
assert.strictEqual(err.syscall, 'connect');
assert.strictEqual(err.localPort, options.localPort);
assert.strictEqual(err.localAddress, options.localAddress);
assert.strictEqual(
err.message,
`connect ${err.code} ${err.address}:${err.port} ` +
`- Local (${err.localAddress}:${err.localPort})`
);
}
const clientIPv4 = net.connect(optionsIPv4);
clientIPv4.on('error', common.mustCall((err) => onError(err, optionsIPv4)));
if (!common.hasIPv6) {
common.printSkipMessage('ipv6 part of test, no IPv6 support');
return;
}
const clientIPv6 = net.connect(optionsIPv6);
clientIPv6.on('error', common.mustCall((err) => onError(err, optionsIPv6)));

View File

@ -0,0 +1,67 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
const cluster = require('cluster');
const net = require('net');
if (cluster.isPrimary) {
const worker1 = cluster.fork();
worker1.on('message', common.mustCall(function(msg) {
assert.strictEqual(msg, 'success');
const worker2 = cluster.fork();
worker2.on('message', common.mustCall(function(msg) {
assert.strictEqual(msg, 'server2:EADDRINUSE');
worker1.kill();
worker2.kill();
}));
}));
} else {
const server1 = net.createServer(common.mustNotCall());
const server2 = net.createServer(common.mustNotCall());
server1.on('error', function(err) {
// no errors expected
process.send(`server1:${err.code}`);
});
server2.on('error', function(err) {
// An error is expected on the second worker
process.send(`server2:${err.code}`);
});
server1.listen({
host: 'localhost',
port: common.PORT,
exclusive: false
}, common.mustCall(function() {
server2.listen({ port: common.PORT + 1, exclusive: true },
common.mustCall(function() {
// The first worker should succeed
process.send('success');
})
);
}));
}

View File

@ -0,0 +1,20 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
const server = net.createServer(function(socket) {
assert.strictEqual(socket.remotePort, common.PORT);
socket.end();
socket.on('close', function() {
server.close();
});
}).listen(0).on('listening', function() {
const client = net.connect({
host: '127.0.0.1',
port: this.address().port,
localPort: common.PORT,
}).on('connect', function() {
assert.strictEqual(client.localPort, common.PORT);
});
});

View File

@ -0,0 +1,43 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const net = require('net');
const assert = require('assert');
const N = 20;
let disconnectCount = 0;
const c = net.createConnection(common.PORT);
c.on('connect', common.mustNotCall('client should not have connected'));
c.on('error', common.mustCall((error) => {
// Family autoselection might be skipped if only a single address is returned by DNS.
const actualError = Array.isArray(error.errors) ? error.errors[0] : error;
assert.strictEqual(actualError.code, 'ECONNREFUSED');
}, N + 1));
c.on('close', common.mustCall(() => {
if (disconnectCount++ < N)
c.connect(common.PORT); // reconnect
}, N + 1));

View File

@ -0,0 +1,75 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
// Make sure the net module's server doesn't throw an error when handling
// responses that are either too long or too small (especially on Windows)
// https://github.com/nodejs/node-v0.x-archive/issues/1697
const net = require('net');
const cp = require('child_process');
if (process.argv[2] === 'server') {
// Server
const server = net.createServer(function(conn) {
conn.on('data', function(data) {
console.log(`server received ${data.length} bytes`);
});
conn.on('close', function() {
server.close();
});
});
server.listen(common.PORT, '127.0.0.1', function() {
console.log('Server running.');
});
} else {
// Client
const serverProcess = cp.spawn(process.execPath, [process.argv[1], 'server']);
serverProcess.stdout.pipe(process.stdout);
serverProcess.stderr.pipe(process.stdout);
serverProcess.stdout.once('data', function() {
const client = net.createConnection(common.PORT, '127.0.0.1');
client.on('connect', function() {
const alot = Buffer.allocUnsafe(1024);
const alittle = Buffer.allocUnsafe(1);
for (let i = 0; i < 100; i++) {
client.write(alot);
}
// Block the event loop for 1 second
const start = (new Date()).getTime();
while ((new Date()).getTime() < start + 1000);
client.write(alittle);
client.destroySoon();
});
});
}

View File

@ -0,0 +1,113 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
// Test on IPv4 Server
{
const family = 'IPv4';
const server = net.createServer();
server.on('error', common.mustNotCall());
server
.listen(common.PORT + 1, common.localhostIPv4, common.mustCall(() => {
const address4 = server.address();
assert.strictEqual(address4.address, common.localhostIPv4);
assert.strictEqual(address4.port, common.PORT + 1);
assert.strictEqual(address4.family, family);
server.close();
}));
}
if (!common.hasIPv6) {
common.printSkipMessage('ipv6 part of test, no IPv6 support');
return;
}
const family6 = 'IPv6';
const anycast6 = '::';
// Test on IPv6 Server
{
const localhost = '::1';
const server = net.createServer();
server.on('error', common.mustNotCall());
server.listen(common.PORT + 2, localhost, common.mustCall(() => {
const address = server.address();
assert.strictEqual(address.address, localhost);
assert.strictEqual(address.port, common.PORT + 2);
assert.strictEqual(address.family, family6);
server.close();
}));
}
// Test without hostname or ip
{
const server = net.createServer();
server.on('error', common.mustNotCall());
// Specify the port number
server.listen(common.PORT + 3, common.mustCall(() => {
const address = server.address();
assert.strictEqual(address.address, anycast6);
assert.strictEqual(address.port, common.PORT + 3);
assert.strictEqual(address.family, family6);
server.close();
}));
}
// Test without hostname or port
{
const server = net.createServer();
server.on('error', common.mustNotCall());
// Don't specify the port number
server.listen(common.mustCall(() => {
const address = server.address();
assert.strictEqual(address.address, anycast6);
assert.strictEqual(address.family, family6);
server.close();
}));
}
// Test without hostname, but with a false-y port
{
const server = net.createServer();
server.on('error', common.mustNotCall());
// Specify a false-y port number
server.listen(0, common.mustCall(() => {
const address = server.address();
assert.strictEqual(address.address, anycast6);
assert.strictEqual(address.family, family6);
server.close();
}));
}

View File

@ -0,0 +1,64 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
// With only a callback, server should get a port assigned by the OS
{
const server = net.createServer(common.mustNotCall());
server.listen(common.mustCall(function() {
assert.ok(server.address().port > 100);
server.close();
}));
}
// No callback to listen(), assume we can bind in 100 ms
{
const server = net.createServer(common.mustNotCall());
server.listen(common.PORT);
setTimeout(function() {
const address = server.address();
assert.strictEqual(address.port, common.PORT);
if (address.family === 'IPv6')
assert.strictEqual(server._connectionKey, `6::::${address.port}`);
else
assert.strictEqual(server._connectionKey, `4:0.0.0.0:${address.port}`);
server.close();
}, 100);
}
// Callback to listen()
{
const server = net.createServer(common.mustNotCall());
server.listen(common.PORT + 1, common.mustCall(function() {
assert.strictEqual(server.address().port, common.PORT + 1);
server.close();
}));
}
// Backlog argument
{
const server = net.createServer(common.mustNotCall());
server.listen(common.PORT + 2, '0.0.0.0', 127, common.mustCall(function() {
assert.strictEqual(server.address().port, common.PORT + 2);
server.close();
}));
}
// Backlog argument without host argument
{
const server = net.createServer(common.mustNotCall());
server.listen(common.PORT + 3, 127, common.mustCall(function() {
assert.strictEqual(server.address().port, common.PORT + 3);
server.close();
}));
}

View File

@ -0,0 +1,131 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
const dns = require('dns');
const { mock } = require('node:test');
if (!common.hasIPv6) {
common.printSkipMessage('IPv6 support is required for this test');
return;
}
// Test on IPv6 Server, dns.lookup throws an error
{
mock.method(dns, 'lookup', (hostname, options, callback) => {
callback(new Error('Mocked error'));
});
const host = 'ipv6-link-local';
const server = net.createServer();
server.on('error', common.mustCall((e) => {
assert.strictEqual(e.message, 'Mocked error');
}));
server.listen(common.PORT + 2, host);
}
// Test on IPv6 Server, server.listen throws an error
{
mock.method(dns, 'lookup', (hostname, options, callback) => {
if (hostname === 'ipv6-link-local') {
callback(null, [{ address: 'fe80::1', family: 6 }]);
} else {
dns.lookup.wrappedMethod(hostname, options, callback);
}
});
const host = 'ipv6-link-local';
const server = net.createServer();
server.on('error', common.mustCall((e) => {
assert.strictEqual(e.address, 'fe80::1');
assert.strictEqual(e.syscall, 'listen');
}));
server.listen(common.PORT + 2, host);
}
// Test on IPv6 Server, picks 127.0.0.1 between that and a bunch of link-local addresses
{
mock.method(dns, 'lookup', (hostname, options, callback) => {
if (hostname === 'ipv6-link-local-with-many-entries') {
callback(null, [
{ address: 'fe80::1', family: 6 },
{ address: 'fe80::abcd:1234', family: 6 },
{ address: 'fe80::1ff:fe23:4567:890a', family: 6 },
{ address: 'fe80::200:5aee:feaa:20a2', family: 6 },
{ address: 'fe80::f2de:f1ff:fe2b:3c4b', family: 6 },
{ address: 'fe81::1', family: 6 },
{ address: 'fe82::abcd:1234', family: 6 },
{ address: 'fe83::1ff:fe23:4567:890a', family: 6 },
{ address: 'fe84::200:5aee:feaa:20a2', family: 6 },
{ address: 'fe85::f2de:f1ff:fe2b:3c4b', family: 6 },
{ address: 'fe86::1', family: 6 },
{ address: 'fe87::abcd:1234', family: 6 },
{ address: 'fe88::1ff:fe23:4567:890a', family: 6 },
{ address: 'fe89::200:5aee:feaa:20a2', family: 6 },
{ address: 'fe8a::f2de:f1ff:fe2b:3c4b', family: 6 },
{ address: 'fe8b::1', family: 6 },
{ address: 'fe8c::abcd:1234', family: 6 },
{ address: 'fe8d::1ff:fe23:4567:890a', family: 6 },
{ address: 'fe8e::200:5aee:feaa:20a2', family: 6 },
{ address: 'fe8f::f2de:f1ff:fe2b:3c4b', family: 6 },
{ address: 'fea0::1', family: 6 },
{ address: 'febf::abcd:1234', family: 6 },
{ address: 'febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff', family: 6 },
{ address: '127.0.0.1', family: 4 },
]);
} else {
dns.lookup.wrappedMethod(hostname, options, callback);
}
});
const host = 'ipv6-link-local-with-many-entries';
const server = net.createServer();
server.on('error', common.mustNotCall());
server.listen(common.PORT + 3, host, common.mustCall(() => {
const address = server.address();
assert.strictEqual(address.address, '127.0.0.1');
assert.strictEqual(address.port, common.PORT + 3);
assert.strictEqual(address.family, 'IPv4');
server.close();
}));
}
// Test on IPv6 Server, picks ::1 because the other address is a link-local address
{
const host = 'ipv6-link-local-with-double-entry';
const validIpv6Address = '::1';
mock.method(dns, 'lookup', (hostname, options, callback) => {
if (hostname === 'ipv6-link-local-with-double-entry') {
callback(null, [
{ address: 'fe80::1', family: 6 },
{ address: validIpv6Address, family: 6 },
]);
} else {
dns.lookup.wrappedMethod(hostname, options, callback);
}
});
const server = net.createServer();
server.on('error', common.mustNotCall());
server.listen(common.PORT + 4, host, common.mustCall(() => {
const address = server.address();
assert.strictEqual(address.address, validIpv6Address);
assert.strictEqual(address.port, common.PORT + 4);
assert.strictEqual(address.family, 'IPv6');
server.close();
}));
}

View File

@ -0,0 +1,66 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
if (process.argv[2] !== 'child') {
const spawn = require('child_process').spawn;
const child = spawn(process.execPath, [__filename, 'child'], {
stdio: 'pipe'// 'inherit'
});
const timer = setTimeout(function() {
throw new Error('child is hung');
}, common.platformTimeout(3000));
child.on('exit', common.mustCall(function(code) {
assert.strictEqual(code, 0);
clearTimeout(timer);
}));
} else {
const domain = require('domain');
const d = domain.create();
// In the error handler, we trigger several MakeCallback events
d.on('error', function() {
console.log('a');
console.log('b');
console.log('c');
console.log('d');
console.log('e');
f();
});
function f() {
process.nextTick(function() {
d.run(function() {
throw new Error('x');
});
});
}
f();
setImmediate(function() {
console.error('broke in!');
process.exit(0);
});
}

View File

@ -0,0 +1,168 @@
'use strict';
require('../common');
const { performance } = require('perf_hooks');
// Get the start time as soon as possible.
const testStartTime = performance.now();
const assert = require('assert');
const { writeSync } = require('fs');
const { isMainThread } = require('worker_threads');
// Use writeSync to stdout to avoid disturbing the loop.
function log(str) {
writeSync(1, str + '\n');
}
assert(performance);
assert(performance.nodeTiming);
assert.strictEqual(typeof performance.timeOrigin, 'number');
assert(testStartTime > 0, `${testStartTime} <= 0`);
// Use a fairly large epsilon value, since we can only guarantee that the node
// process started up in 15 seconds.
assert(testStartTime < 15000, `${testStartTime} >= 15000`);
// Use different ways to calculate process uptime to check that
// performance.timeOrigin and performance.now() are in reasonable range.
const epsilon = 50;
{
const uptime1 = Date.now() - performance.timeOrigin;
const uptime2 = performance.now();
const uptime3 = process.uptime() * 1000;
assert(Math.abs(uptime1 - uptime2) < epsilon,
`Date.now() - performance.timeOrigin (${uptime1}) - ` +
`performance.now() (${uptime2}) = ` +
`${uptime1 - uptime2} >= +- ${epsilon}`);
assert(Math.abs(uptime1 - uptime3) < epsilon,
`Date.now() - performance.timeOrigin (${uptime1}) - ` +
`process.uptime() * 1000 (${uptime3}) = ` +
`${uptime1 - uptime3} >= +- ${epsilon}`);
}
assert.strictEqual(performance.nodeTiming.name, 'node');
assert.strictEqual(performance.nodeTiming.entryType, 'node');
// If timing.duration gets copied into the argument instead of being computed
// via the getter, this should be called right after timing is created.
function checkNodeTiming(timing) {
// Calculate the difference between now() and duration as soon as possible.
const now = performance.now();
const delta = Math.abs(now - timing.duration);
log(JSON.stringify(timing, null, 2));
// Check that the properties are still reasonable.
assert.strictEqual(timing.name, 'node');
assert.strictEqual(timing.entryType, 'node');
// Check that duration is positive and practically the same as
// performance.now() i.e. measures Node.js instance up time.
assert.strictEqual(typeof timing.duration, 'number');
assert(timing.duration > 0, `timing.duration ${timing.duration} <= 0`);
assert(delta < 10,
`now (${now}) - timing.duration (${timing.duration}) = ${delta} >= ${10}`);
// Check that the following fields do not change.
assert.strictEqual(timing.startTime, initialTiming.startTime);
assert.strictEqual(timing.nodeStart, initialTiming.nodeStart);
assert.strictEqual(timing.v8Start, initialTiming.v8Start);
assert.strictEqual(timing.environment, initialTiming.environment);
assert.strictEqual(timing.bootstrapComplete, initialTiming.bootstrapComplete);
assert.strictEqual(typeof timing.loopStart, 'number');
assert.strictEqual(typeof timing.loopExit, 'number');
}
log('check initial nodeTiming');
// Copy all the values from the getters.
const initialTiming = { ...performance.nodeTiming };
checkNodeTiming(initialTiming);
{
const {
startTime,
nodeStart,
v8Start,
environment,
bootstrapComplete,
} = initialTiming;
assert.strictEqual(startTime, 0);
assert.strictEqual(typeof nodeStart, 'number');
assert(nodeStart > 0, `nodeStart ${nodeStart} <= 0`);
// The whole process starts before this test starts.
assert(nodeStart < testStartTime,
`nodeStart ${nodeStart} >= ${testStartTime}`);
assert.strictEqual(typeof v8Start, 'number');
assert(v8Start > 0, `v8Start ${v8Start} <= 0`);
// V8 starts after the process starts.
assert(v8Start > nodeStart, `v8Start ${v8Start} <= ${nodeStart}`);
// V8 starts before this test starts.
assert(v8Start < testStartTime,
`v8Start ${v8Start} >= ${testStartTime}`);
assert.strictEqual(typeof environment, 'number');
assert(environment > 0, `environment ${environment} <= 0`);
// Environment starts after V8 starts.
assert(environment > v8Start,
`environment ${environment} <= ${v8Start}`);
// Environment starts before this test starts.
assert(environment < testStartTime,
`environment ${environment} >= ${testStartTime}`);
assert.strictEqual(typeof bootstrapComplete, 'number');
assert(bootstrapComplete > 0, `bootstrapComplete ${bootstrapComplete} <= 0`);
// Bootstrap completes after environment starts.
assert(bootstrapComplete > environment,
`bootstrapComplete ${bootstrapComplete} <= ${environment}`);
// Bootstrap completes before this test starts.
assert(bootstrapComplete < testStartTime,
`bootstrapComplete ${bootstrapComplete} >= ${testStartTime}`);
}
assert.strictEqual(initialTiming.loopExit, -1);
function checkValue(timing, name, min, max) {
const value = timing[name];
assert(value > 0, `${name} ${value} <= 0`);
// Loop starts after bootstrap completes.
assert(value > min,
`${name} ${value} <= ${min}`);
assert(value < max, `${name} ${value} >= ${max}`);
}
let loopStart = initialTiming.loopStart;
if (isMainThread) {
// In the main thread, the loop does not start until we start an operation
// that requires it, e.g. setTimeout().
assert.strictEqual(initialTiming.loopStart, -1);
log('Start timer');
setTimeout(() => {
log('Check nodeTiming in timer');
const timing = { ...performance.nodeTiming };
checkNodeTiming(timing);
// Loop should start after we fire the timeout, and before we call
// performance.now() here.
loopStart = timing.loopStart;
checkValue(timing, 'loopStart', initialTiming.duration, performance.now());
}, 1000);
} else {
// In the worker, the loop always starts before the user code is evaluated,
// and after bootstrap completes.
checkValue(initialTiming,
'loopStart',
initialTiming.bootstrapComplete,
testStartTime);
}
process.on('exit', () => {
log('Check nodeTiming in process exit event');
const timing = { ...performance.nodeTiming };
checkNodeTiming(timing);
// Check that loopStart does not change.
assert.strictEqual(timing.loopStart, loopStart);
checkValue(timing,
'loopExit',
loopStart,
performance.now());
});

View File

@ -0,0 +1,110 @@
// Flags: --expose-gc --expose-internals
'use strict';
const common = require('../common');
const assert = require('assert');
const os = require('os');
const {
monitorEventLoopDelay
} = require('perf_hooks');
const { sleep } = require('internal/util');
{
const histogram = monitorEventLoopDelay();
assert(histogram);
assert(histogram.enable());
assert(!histogram.enable());
histogram.reset();
assert(histogram.disable());
assert(!histogram.disable());
}
{
[null, 'a', 1, false, Infinity].forEach((i) => {
assert.throws(
() => monitorEventLoopDelay(i),
{
name: 'TypeError',
code: 'ERR_INVALID_ARG_TYPE'
}
);
});
[null, 'a', false, {}, []].forEach((i) => {
assert.throws(
() => monitorEventLoopDelay({ resolution: i }),
{
name: 'TypeError',
code: 'ERR_INVALID_ARG_TYPE'
}
);
});
[-1, 0, 2 ** 53, Infinity].forEach((i) => {
assert.throws(
() => monitorEventLoopDelay({ resolution: i }),
{
name: 'RangeError',
code: 'ERR_OUT_OF_RANGE'
}
);
});
}
{
const s390x = os.arch() === 's390x';
const histogram = monitorEventLoopDelay({ resolution: 1 });
histogram.enable();
let m = 5;
if (s390x) {
m = m * 2;
}
function spinAWhile() {
sleep(1000);
if (--m > 0) {
setTimeout(spinAWhile, common.platformTimeout(500));
} else {
histogram.disable();
// The values are non-deterministic, so we just check that a value is
// present, as opposed to a specific value.
assert(histogram.min > 0);
assert(histogram.max > 0);
assert(histogram.stddev > 0);
assert(histogram.mean > 0);
assert(histogram.percentiles.size > 0);
for (let n = 1; n < 100; n = n + 0.1) {
assert(histogram.percentile(n) >= 0);
}
histogram.reset();
assert.strictEqual(histogram.min, 9223372036854776000);
assert.strictEqual(histogram.max, 0);
assert(Number.isNaN(histogram.stddev));
assert(Number.isNaN(histogram.mean));
assert.strictEqual(histogram.percentiles.size, 1);
['a', false, {}, []].forEach((i) => {
assert.throws(
() => histogram.percentile(i),
{
name: 'TypeError',
code: 'ERR_INVALID_ARG_TYPE'
}
);
});
[-1, 0, 101, NaN].forEach((i) => {
assert.throws(
() => histogram.percentile(i),
{
name: 'RangeError',
code: 'ERR_OUT_OF_RANGE'
}
);
});
}
}
spinAWhile();
}
// Make sure that the histogram instances can be garbage-collected without
// and not just implicitly destroyed when the Environment is torn down.
process.on('exit', global.gc);

View File

@ -0,0 +1,115 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const net = require('net');
const webPort = common.PORT;
const tcpPort = webPort + 1;
const bufferSize = 5 * 1024 * 1024;
let listenCount = 0;
let gotThanks = false;
let tcpLengthSeen = 0;
// 5MB of random buffer.
const buffer = Buffer.allocUnsafe(bufferSize);
for (let i = 0; i < buffer.length; i++) {
buffer[i] = parseInt(Math.random() * 10000) % 256;
}
const web = http.Server(common.mustCall((req, res) => {
web.close();
const socket = net.Stream();
socket.connect(tcpPort);
socket.on('connect', common.mustCall());
req.pipe(socket);
req.on('end', common.mustCall(() => {
res.writeHead(200);
res.write('thanks');
res.end();
}));
req.connection.on('error', (e) => {
assert.ifError(e);
});
}));
web.listen(webPort, startClient);
const tcp = net.Server(common.mustCall((s) => {
tcp.close();
let i = 0;
s.on('data', (d) => {
tcpLengthSeen += d.length;
for (let j = 0; j < d.length; j++) {
assert.strictEqual(d[j], buffer[i]);
i++;
}
});
s.on('end', common.mustCall(() => {
s.end();
}));
s.on('error', (e) => {
assert.ifError(e);
});
}));
tcp.listen(tcpPort, startClient);
function startClient() {
listenCount++;
if (listenCount < 2) return;
const req = http.request({
port: common.PORT,
method: 'GET',
path: '/',
headers: { 'content-length': buffer.length }
}, common.mustCall((res) => {
res.setEncoding('utf8');
res.on('data', common.mustCall((string) => {
assert.strictEqual(string, 'thanks');
gotThanks = true;
}));
}));
req.write(buffer);
req.end();
}
process.on('exit', () => {
assert.ok(gotThanks);
assert.strictEqual(tcpLengthSeen, bufferSize);
});

View File

@ -0,0 +1,22 @@
'use strict';
const common = require('../common');
const { spawnSync } = require('child_process');
const { strictEqual } = require('assert');
// FIXME add sunos support
if (common.isSunOS)
common.skip(`Unsupported platform [${process.platform}]`);
// FIXME add IBMi support
if (common.isIBMi)
common.skip('Unsupported platform IBMi');
// Explicitly assigning to process.title before starting the child process
// is necessary otherwise *its* process.title is whatever the last
// SetConsoleTitle() call in our process tree set it to.
// Can be removed when https://github.com/libuv/libuv/issues/2667 is fixed.
if (common.isWindows)
process.title = process.execPath;
const xs = 'x'.repeat(1024);
const proc = spawnSync(process.execPath, ['-p', 'process.title', xs]);
strictEqual(proc.stdout.toString().trim(), process.execPath);

View File

@ -0,0 +1,36 @@
'use strict';
require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const execFile = require('child_process').execFile;
const warnmod = require.resolve(fixtures.path('warnings.js'));
const node = process.execPath;
const normal = [warnmod];
const noWarn = ['--no-warnings', warnmod];
const traceWarn = ['--trace-warnings', warnmod];
const warningMessage = /^\(.+\)\sWarning: a bad practice warning/;
execFile(node, normal, function(er, stdout, stderr) {
// Show Process Warnings
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.match(stderr, warningMessage);
});
execFile(node, noWarn, function(er, stdout, stderr) {
// Hide Process Warnings
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.doesNotMatch(stderr, warningMessage);
});
execFile(node, traceWarn, function(er, stdout, stderr) {
// Show Warning Trace
assert.strictEqual(er, null);
assert.strictEqual(stdout, '');
assert.match(stderr, warningMessage);
assert.match(stderr, /at Object\.<anonymous>\s\(.+warnings\.js:3:9\)/);
});

View File

@ -0,0 +1,59 @@
'use strict';
require('../common');
const assert = require('assert');
const spawn = require('child_process').spawn;
const child = spawn(process.execPath, [ '-i' ], {
stdio: [null, null, 2]
});
let stdout = '';
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(c) {
process.stdout.write(c);
stdout += c;
if (stdout.includes('> THROW 2'))
child.stdin.end();
});
child.stdin.write = function(original) {
return function(c) {
process.stderr.write(c);
return original.call(child.stdin, c);
};
}(child.stdin.write);
child.stdout.once('data', function() {
child.stdin.write('let throws = 0;');
child.stdin.write('process.on("exit",function(){console.log(throws)});');
child.stdin.write('function thrower(){console.log("THROW",throws++);XXX};');
child.stdin.write('setTimeout(thrower);""\n');
setTimeout(fsTest, 50);
function fsTest() {
const f = JSON.stringify(__filename);
child.stdin.write(`fs.readFile(${f}, thrower);\n`);
setTimeout(eeTest, 50);
}
function eeTest() {
child.stdin.write('setTimeout(function() {\n' +
' const events = require("events");\n' +
' let e = new events.EventEmitter;\n' +
' process.nextTick(function() {\n' +
' e.on("x", thrower);\n' +
' setTimeout(function() {\n' +
' e.emit("x");\n' +
' });\n' +
' });\n' +
'});"";\n');
}
});
child.on('close', function(c) {
assert.strictEqual(c, 0);
// Make sure we got 3 throws, in the end.
const lastLine = stdout.trim().split(/\r?\n/).pop();
assert.strictEqual(lastLine, '> 3');
});

View File

@ -0,0 +1,74 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
// We've experienced a regression where the module loader stats a bunch of
// directories on require() even if it's been called before. The require()
// should caching the request.
require('../common');
const fs = require('fs');
const assert = require('assert');
const { fixturesDir } = require('../common/fixtures');
let counter = 0;
// Switch out the two stat implementations so that they increase a counter
// each time they are called.
const _statSync = fs.statSync;
const _stat = fs.stat;
fs.statSync = function() {
counter++;
return _statSync.apply(this, arguments);
};
fs.stat = function() {
counter++;
return _stat.apply(this, arguments);
};
// Load the module 'a' and 'http' once. It should become cached.
require(`${fixturesDir}/a`);
require('../fixtures/a.js');
require('./../fixtures/a.js');
require('http');
console.log(`counterBefore = ${counter}`);
const counterBefore = counter;
// Now load the module a bunch of times with equivalent paths.
// stat should not be called.
for (let i = 0; i < 100; i++) {
require(`${fixturesDir}/a`);
require('../fixtures/a.js');
require('./../fixtures/a.js');
}
// Do the same with a built-in module
for (let i = 0; i < 100; i++) {
require('http');
}
console.log(`counterAfter = ${counter}`);
const counterAfter = counter;
assert.strictEqual(counterBefore, counterAfter);

View File

@ -0,0 +1,29 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
// A test to ensure that preload modules are given a chance to execute before
// resolving the main entry point with --inspect-brk active.
const assert = require('assert');
const cp = require('child_process');
const path = require('path');
function test(execArgv) {
const child = cp.spawn(process.execPath, execArgv);
child.stderr.once('data', common.mustCall(function() {
child.kill('SIGTERM');
}));
child.on('exit', common.mustCall(function(code, signal) {
assert.strictEqual(signal, 'SIGTERM');
}));
}
test([
'--require',
path.join(__dirname, '../fixtures/test-resolution-inspect-brk-resolver.js'),
'--inspect-brk',
'../fixtures/test-resolution-inspect-resolver-main.ext',
]);

View File

@ -0,0 +1,164 @@
import * as common from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import assert from 'node:assert';
common.skipIfInspectorDisabled();
const debuggerPort = common.getPort();
async function spawnRunner({ execArgv, expectedPort, expectedHost, expectedInitialPort, inspectPort }) {
const { code, signal } = await common.spawnPromisified(
process.execPath,
['--expose-internals', '--no-warnings', ...execArgv, fixtures.path('test-runner/run_inspect.js')], {
env: { ...process.env,
expectedPort,
inspectPort,
expectedHost,
expectedInitialPort }
});
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}
let offset = 0;
const defaultPortCase = spawnRunner({
execArgv: ['--inspect'],
expectedPort: 9230,
});
await spawnRunner({
execArgv: ['--inspect=65535'],
expectedPort: 1024,
});
let port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
expectedPort: port + 1,
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: ['--inspect', `--inspect-port=${port}`],
expectedPort: port + 1,
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: ['--inspect', `--debug-port=${port}`],
expectedPort: port + 1,
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=0.0.0.0:${port}`],
expectedPort: port + 1, expectedHost: '0.0.0.0',
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=127.0.0.1:${port}`],
expectedPort: port + 1, expectedHost: '127.0.0.1'
});
if (common.hasIPv6) {
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=[::]:${port}`],
expectedPort: port + 1, expectedHost: '::'
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=[::1]:${port}`],
expectedPort: port + 1, expectedHost: '::1'
});
}
// These tests check that setting inspectPort in run
// would take effect and override port incrementing behavior
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: port + 2,
expectedPort: port + 2,
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 'addTwo',
expectedPort: port + 2,
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 'string',
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 'null',
expectedPort: port + 1,
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 'bignumber',
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 'negativenumber',
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 'bignumberfunc'
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 'strfunc',
});
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: [`--inspect=${port}`],
inspectPort: 0,
expectedInitialPort: 0,
});
await defaultPortCase;
port = debuggerPort + offset++ * 5;
await spawnRunner({
execArgv: ['--inspect'],
inspectPort: port + 2,
expectedInitialPort: port + 2,
});

View File

@ -0,0 +1,70 @@
'use strict';
const common = require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests the snapshot support in single executable applications.
const tmpdir = require('../common/tmpdir');
const { copyFileSync, writeFileSync, existsSync } = require('fs');
const {
spawnSyncAndExitWithoutError,
} = require('../common/child_process');
const assert = require('assert');
const fixtures = require('../common/fixtures');
tmpdir.refresh();
if (!tmpdir.hasEnoughSpace(120 * 1024 * 1024)) {
common.skip('Not enough disk space');
}
const configFile = tmpdir.resolve('sea-config.json');
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
{
tmpdir.refresh();
copyFileSync(fixtures.path('sea', 'get-asset-raw.js'), tmpdir.resolve('sea.js'));
copyFileSync(fixtures.path('person.jpg'), tmpdir.resolve('person.jpg'));
writeFileSync(configFile, `
{
"main": "sea.js",
"output": "sea-prep.blob",
"assets": {
"person.jpg": "person.jpg"
}
}
`, 'utf8');
spawnSyncAndExitWithoutError(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
},
cwd: tmpdir.path
});
assert(existsSync(seaPrepBlob));
generateSEA(outputFile, process.execPath, seaPrepBlob);
spawnSyncAndExitWithoutError(
outputFile,
{
env: {
...process.env,
NODE_DEBUG_NATIVE: 'SEA',
__TEST_PERSON_JPG: fixtures.path('person.jpg'),
}
},
);
}

View File

@ -0,0 +1,129 @@
'use strict';
const common = require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests the snapshot support in single executable applications.
const tmpdir = require('../common/tmpdir');
const { copyFileSync, writeFileSync, existsSync } = require('fs');
const {
spawnSyncAndAssert,
spawnSyncAndExit,
spawnSyncAndExitWithoutError,
} = require('../common/child_process');
const assert = require('assert');
const fixtures = require('../common/fixtures');
tmpdir.refresh();
if (!tmpdir.hasEnoughSpace(120 * 1024 * 1024)) {
common.skip('Not enough disk space');
}
const configFile = tmpdir.resolve('sea-config.json');
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
{
tmpdir.refresh();
copyFileSync(fixtures.path('sea', 'get-asset.js'), tmpdir.resolve('sea.js'));
writeFileSync(configFile, `
{
"main": "sea.js",
"output": "sea-prep.blob",
"assets": "invalid"
}
`);
spawnSyncAndExit(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
cwd: tmpdir.path
},
{
status: 1,
signal: null,
stderr: /"assets" field of sea-config\.json is not a map of strings/
});
}
{
tmpdir.refresh();
copyFileSync(fixtures.path('sea', 'get-asset.js'), tmpdir.resolve('sea.js'));
writeFileSync(configFile, `
{
"main": "sea.js",
"output": "sea-prep.blob",
"assets": {
"nonexistent": "nonexistent.txt"
}
}
`);
spawnSyncAndExit(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
cwd: tmpdir.path
},
{
status: 1,
signal: null,
stderr: /Cannot read asset nonexistent\.txt: no such file or directory/
});
}
{
tmpdir.refresh();
copyFileSync(fixtures.path('sea', 'get-asset.js'), tmpdir.resolve('sea.js'));
copyFileSync(fixtures.utf8TestTextPath, tmpdir.resolve('utf8_test_text.txt'));
copyFileSync(fixtures.path('person.jpg'), tmpdir.resolve('person.jpg'));
writeFileSync(configFile, `
{
"main": "sea.js",
"output": "sea-prep.blob",
"assets": {
"utf8_test_text.txt": "utf8_test_text.txt",
"person.jpg": "person.jpg"
}
}
`, 'utf8');
spawnSyncAndExitWithoutError(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
},
cwd: tmpdir.path
});
assert(existsSync(seaPrepBlob));
generateSEA(outputFile, process.execPath, seaPrepBlob);
spawnSyncAndAssert(
outputFile,
{
env: {
...process.env,
NODE_DEBUG_NATIVE: 'SEA',
__TEST_PERSON_JPG: fixtures.path('person.jpg'),
__TEST_UTF8_TEXT_PATH: fixtures.path('utf8_test_text.txt'),
}
},
{
trim: true,
stdout: fixtures.utf8TestText,
}
);
}

View File

@ -0,0 +1,67 @@
'use strict';
require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests the creation of a single executable application which has the
// experimental SEA warning disabled.
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { copyFileSync, writeFileSync, existsSync } = require('fs');
const { spawnSyncAndAssert, spawnSyncAndExitWithoutError } = require('../common/child_process');
const { join } = require('path');
const assert = require('assert');
const inputFile = fixtures.path('sea.js');
const requirableFile = tmpdir.resolve('requirable.js');
const configFile = tmpdir.resolve('sea-config.json');
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
tmpdir.refresh();
writeFileSync(requirableFile, `
module.exports = {
hello: 'world',
};
`);
writeFileSync(configFile, `
{
"main": "sea.js",
"output": "sea-prep.blob",
"disableExperimentalSEAWarning": true
}
`);
// Copy input to working directory
copyFileSync(inputFile, tmpdir.resolve('sea.js'));
spawnSyncAndExitWithoutError(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{ cwd: tmpdir.path });
assert(existsSync(seaPrepBlob));
generateSEA(outputFile, process.execPath, seaPrepBlob);
spawnSyncAndAssert(
outputFile,
[ '-a', '--b=c', 'd' ],
{
env: {
COMMON_DIRECTORY: join(__dirname, '..', 'common'),
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
}
},
{
stdout: 'Hello, world! 😊\n'
});

View File

@ -0,0 +1,63 @@
'use strict';
const common = require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests the creation of a single executable application with an empty
// script.
const tmpdir = require('../common/tmpdir');
const { writeFileSync, existsSync } = require('fs');
const { spawnSyncAndExitWithoutError } = require('../common/child_process');
const assert = require('assert');
const configFile = tmpdir.resolve('sea-config.json');
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
tmpdir.refresh();
writeFileSync(tmpdir.resolve('empty.js'), '', 'utf-8');
writeFileSync(configFile, `
{
"main": "empty.js",
"output": "sea-prep.blob"
}
`);
spawnSyncAndExitWithoutError(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{ cwd: tmpdir.path });
assert(existsSync(seaPrepBlob));
// Verify the workflow.
try {
generateSEA(outputFile, process.execPath, seaPrepBlob, true);
} catch (e) {
if (/Cannot copy/.test(e.message)) {
common.skip(e.message);
} else if (common.isWindows) {
if (/Cannot sign/.test(e.message) || /Cannot find signtool/.test(e.message)) {
common.skip(e.message);
}
}
throw e;
}
spawnSyncAndExitWithoutError(
outputFile,
{
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
}
});

View File

@ -0,0 +1,78 @@
'use strict';
require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests "useCodeCache" is ignored when "useSnapshot" is true.
const tmpdir = require('../common/tmpdir');
const { writeFileSync, existsSync } = require('fs');
const {
spawnSyncAndAssert,
} = require('../common/child_process');
const { join } = require('path');
const assert = require('assert');
const configFile = join(tmpdir.path, 'sea-config.json');
const seaPrepBlob = join(tmpdir.path, 'sea-prep.blob');
const outputFile = join(tmpdir.path, process.platform === 'win32' ? 'sea.exe' : 'sea');
{
tmpdir.refresh();
const code = `
const {
setDeserializeMainFunction,
} = require('v8').startupSnapshot;
setDeserializeMainFunction(() => {
console.log('Hello from snapshot');
});
`;
writeFileSync(join(tmpdir.path, 'snapshot.js'), code, 'utf-8');
writeFileSync(configFile, `
{
"main": "snapshot.js",
"output": "sea-prep.blob",
"useSnapshot": true,
"useCodeCache": true
}
`);
spawnSyncAndAssert(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
cwd: tmpdir.path,
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
},
},
{
stderr: /"useCodeCache" is redundant when "useSnapshot" is true/
}
);
assert(existsSync(seaPrepBlob));
generateSEA(outputFile, process.execPath, seaPrepBlob);
spawnSyncAndAssert(
outputFile,
{
env: {
NODE_DEBUG_NATIVE: 'SEA,MKSNAPSHOT',
...process.env,
}
}, {
stdout: 'Hello from snapshot',
trim: true,
});
}

View File

@ -0,0 +1,80 @@
'use strict';
require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests the snapshot support in single executable applications.
const tmpdir = require('../common/tmpdir');
const { writeFileSync, existsSync } = require('fs');
const {
spawnSyncAndAssert, spawnSyncAndExitWithoutError,
} = require('../common/child_process');
const assert = require('assert');
const configFile = tmpdir.resolve('sea-config.json');
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
{
tmpdir.refresh();
// FIXME(joyeecheung): currently `worker_threads` cannot be loaded during the
// snapshot building process because internal/worker.js is accessing isMainThread at
// the top level (and there are maybe more code that access these at the top-level),
// and have to be loaded in the deserialized snapshot main function.
// Change these states to be accessed on-demand.
const code = `
const {
setDeserializeMainFunction,
} = require('v8').startupSnapshot;
setDeserializeMainFunction(() => {
const { Worker } = require('worker_threads');
new Worker("console.log('Hello from Worker')", { eval: true });
});
`;
writeFileSync(tmpdir.resolve('snapshot.js'), code, 'utf-8');
writeFileSync(configFile, `
{
"main": "snapshot.js",
"output": "sea-prep.blob",
"useSnapshot": true
}
`);
spawnSyncAndExitWithoutError(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
cwd: tmpdir.path,
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
},
});
assert(existsSync(seaPrepBlob));
generateSEA(outputFile, process.execPath, seaPrepBlob);
spawnSyncAndAssert(
outputFile,
{
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
}
},
{
trim: true,
stdout: 'Hello from Worker'
}
);
}

View File

@ -0,0 +1,108 @@
'use strict';
require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests the snapshot support in single executable applications.
const tmpdir = require('../common/tmpdir');
const { writeFileSync, existsSync } = require('fs');
const {
spawnSyncAndAssert,
spawnSyncAndExit,
} = require('../common/child_process');
const assert = require('assert');
const configFile = tmpdir.resolve('sea-config.json');
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
{
tmpdir.refresh();
writeFileSync(tmpdir.resolve('snapshot.js'), '', 'utf-8');
writeFileSync(configFile, `
{
"main": "snapshot.js",
"output": "sea-prep.blob",
"useSnapshot": true
}
`);
spawnSyncAndExit(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
cwd: tmpdir.path
},
{
status: 1,
signal: null,
stderr: /snapshot\.js does not invoke v8\.startupSnapshot\.setDeserializeMainFunction\(\)/
});
}
{
tmpdir.refresh();
const code = `
const {
setDeserializeMainFunction,
} = require('v8').startupSnapshot;
setDeserializeMainFunction(() => {
console.log('Hello from snapshot');
});
`;
writeFileSync(tmpdir.resolve('snapshot.js'), code, 'utf-8');
writeFileSync(configFile, `
{
"main": "snapshot.js",
"output": "sea-prep.blob",
"useSnapshot": true
}
`);
spawnSyncAndAssert(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
cwd: tmpdir.path,
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
},
},
{
stderr: /Single executable application is an experimental feature/
});
assert(existsSync(seaPrepBlob));
generateSEA(outputFile, process.execPath, seaPrepBlob);
spawnSyncAndAssert(
outputFile,
{
env: {
NODE_DEBUG_NATIVE: 'SEA,MKSNAPSHOT',
...process.env,
}
},
{
trim: true,
stdout: 'Hello from snapshot',
stderr(output) {
assert.doesNotMatch(
output,
/Single executable application is an experimental feature/);
}
}
);
}

View File

@ -0,0 +1,73 @@
'use strict';
require('../common');
const {
generateSEA,
skipIfSingleExecutableIsNotSupported,
} = require('../common/sea');
skipIfSingleExecutableIsNotSupported();
// This tests the creation of a single executable application which uses the
// V8 code cache.
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { copyFileSync, writeFileSync, existsSync } = require('fs');
const { spawnSyncAndAssert, spawnSyncAndExitWithoutError } = require('../common/child_process');
const { join } = require('path');
const assert = require('assert');
const inputFile = fixtures.path('sea.js');
const requirableFile = tmpdir.resolve('requirable.js');
const configFile = tmpdir.resolve('sea-config.json');
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
tmpdir.refresh();
writeFileSync(requirableFile, `
module.exports = {
hello: 'world',
};
`);
writeFileSync(configFile, `
{
"main": "sea.js",
"output": "sea-prep.blob",
"useCodeCache": true
}
`);
// Copy input to working directory
copyFileSync(inputFile, tmpdir.resolve('sea.js'));
spawnSyncAndExitWithoutError(
process.execPath,
['--experimental-sea-config', 'sea-config.json'],
{
cwd: tmpdir.path,
env: {
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
},
});
assert(existsSync(seaPrepBlob));
generateSEA(outputFile, process.execPath, seaPrepBlob);
spawnSyncAndAssert(
outputFile,
[ '-a', '--b=c', 'd' ],
{
env: {
COMMON_DIRECTORY: join(__dirname, '..', 'common'),
NODE_DEBUG_NATIVE: 'SEA',
...process.env,
}
},
{
stdout: 'Hello, world! 😊\n'
});

Some files were not shown because too many files have changed in this diff Show More