2017-01-03 13:16:48 -08:00
|
|
|
|
// 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.
|
|
|
|
|
|
2015-05-19 13:00:06 +02:00
|
|
|
|
'use strict';
|
2025-01-22 15:08:23 -08:00
|
|
|
|
const process = globalThis.process; // Some tests tamper with the process globalThis.
|
2019-12-12 10:42:52 -05:00
|
|
|
|
|
2016-10-16 20:40:39 -07:00
|
|
|
|
const assert = require('assert');
|
2019-12-12 10:42:52 -05:00
|
|
|
|
const fs = require('fs');
|
2023-02-23 10:55:04 +01:00
|
|
|
|
const net = require('net');
|
2020-06-20 00:57:27 +08:00
|
|
|
|
// Do not require 'os' until needed so that test-os-checked-function can
|
2019-12-12 10:42:52 -05:00
|
|
|
|
// monkey patch it. If 'os' is required here, that test will fail.
|
|
|
|
|
const path = require('path');
|
2024-11-02 23:24:56 +08:00
|
|
|
|
const { inspect, getCallSites } = require('util');
|
2019-12-12 10:42:52 -05:00
|
|
|
|
const { isMainThread } = require('worker_threads');
|
|
|
|
|
|
2017-12-24 22:38:11 -08:00
|
|
|
|
const tmpdir = require('./tmpdir');
|
2023-01-29 04:23:11 +08:00
|
|
|
|
const bits = ['arm64', 'loong64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x', 'x64']
|
2019-02-25 22:27:55 +01:00
|
|
|
|
.includes(process.arch) ? 64 : 32;
|
|
|
|
|
const hasIntl = !!process.config.variables.v8_enable_i18n_support;
|
2009-10-31 19:02:30 +01:00
|
|
|
|
|
2021-03-17 13:11:05 -07:00
|
|
|
|
const {
|
|
|
|
|
atob,
|
2022-11-21 12:38:12 -05:00
|
|
|
|
btoa,
|
2021-03-17 13:11:05 -07:00
|
|
|
|
} = require('buffer');
|
|
|
|
|
|
2018-12-26 06:14:54 -08:00
|
|
|
|
// Some tests assume a umask of 0o022 so set that up front. Tests that need a
|
|
|
|
|
// different umask will set it themselves.
|
|
|
|
|
//
|
2019-01-15 15:39:43 -05:00
|
|
|
|
// Workers can read, but not set the umask, so check that this is the main
|
|
|
|
|
// thread.
|
|
|
|
|
if (isMainThread)
|
2018-12-26 06:14:54 -08:00
|
|
|
|
process.umask(0o022);
|
|
|
|
|
|
2017-03-24 09:46:44 -07:00
|
|
|
|
const noop = () => {};
|
|
|
|
|
|
2020-01-08 20:52:13 -08:00
|
|
|
|
const hasCrypto = Boolean(process.versions.openssl) &&
|
|
|
|
|
!process.env.NODE_SKIP_CRYPTO;
|
2018-12-20 08:54:40 +01:00
|
|
|
|
|
2025-04-29 22:38:51 +00:00
|
|
|
|
const hasSQLite = Boolean(process.versions.sqlite);
|
|
|
|
|
|
2025-02-19 13:49:30 -08:00
|
|
|
|
const hasQuic = hasCrypto && !!process.config.variables.node_quic;
|
2021-03-09 13:50:08 -08:00
|
|
|
|
|
2023-04-04 00:41:13 +03:00
|
|
|
|
function parseTestFlags(filename = process.argv[1]) {
|
|
|
|
|
// The copyright notice is relatively big and the flags could come afterwards.
|
|
|
|
|
const bytesToRead = 1500;
|
|
|
|
|
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
|
|
|
const fd = fs.openSync(filename, 'r');
|
|
|
|
|
const bytesRead = fs.readSync(fd, buffer, 0, bytesToRead);
|
|
|
|
|
fs.closeSync(fd);
|
|
|
|
|
const source = buffer.toString('utf8', 0, bytesRead);
|
|
|
|
|
|
2023-04-27 09:41:58 +03:00
|
|
|
|
const flagStart = source.search(/\/\/ Flags:\s+--/) + 10;
|
2023-04-04 00:41:13 +03:00
|
|
|
|
|
|
|
|
|
if (flagStart === 9) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
let flagEnd = source.indexOf('\n', flagStart);
|
|
|
|
|
// Normalize different EOL.
|
|
|
|
|
if (source[flagEnd - 1] === '\r') {
|
|
|
|
|
flagEnd--;
|
|
|
|
|
}
|
|
|
|
|
return source
|
|
|
|
|
.substring(flagStart, flagEnd)
|
2023-04-27 09:41:58 +03:00
|
|
|
|
.split(/\s+/)
|
|
|
|
|
.filter(Boolean);
|
2023-04-04 00:41:13 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-06 16:52:33 +01:00
|
|
|
|
// Check for flags. Skip this for workers (both, the `cluster` module and
|
|
|
|
|
// `worker_threads`) and child processes.
|
2019-03-27 13:13:24 +01:00
|
|
|
|
// If the binary was built without-ssl then the crypto flags are
|
|
|
|
|
// invalid (bad option). The test itself should handle this case.
|
2018-12-06 16:52:33 +01:00
|
|
|
|
if (process.argv.length === 2 &&
|
2019-04-16 13:01:33 +02:00
|
|
|
|
!process.env.NODE_SKIP_FLAG_CHECK &&
|
2018-12-06 16:52:33 +01:00
|
|
|
|
isMainThread &&
|
2019-03-27 13:13:24 +01:00
|
|
|
|
hasCrypto &&
|
2022-02-01 09:17:50 +01:00
|
|
|
|
require('cluster').isPrimary &&
|
|
|
|
|
fs.existsSync(process.argv[1])) {
|
2023-04-04 00:41:13 +03:00
|
|
|
|
const flags = parseTestFlags();
|
|
|
|
|
for (const flag of flags) {
|
2023-05-17 15:24:07 +09:00
|
|
|
|
if (!process.execArgv.includes(flag) &&
|
2023-04-04 00:41:13 +03:00
|
|
|
|
// If the binary is build without `intl` the inspect option is
|
|
|
|
|
// invalid. The test itself should handle this case.
|
|
|
|
|
(process.features.inspector || !flag.startsWith('--inspect'))) {
|
|
|
|
|
console.log(
|
|
|
|
|
'NOTE: The test started as a child_process using these flags:',
|
|
|
|
|
inspect(flags),
|
|
|
|
|
'Use NODE_SKIP_FLAG_CHECK to run the test with the original flags.',
|
|
|
|
|
);
|
2025-01-22 16:58:49 -08:00
|
|
|
|
const { spawnSync } = require('child_process');
|
2023-04-04 00:41:13 +03:00
|
|
|
|
const args = [...flags, ...process.execArgv, ...process.argv.slice(1)];
|
|
|
|
|
const options = { encoding: 'utf8', stdio: 'inherit' };
|
|
|
|
|
const result = spawnSync(process.execPath, args, options);
|
|
|
|
|
if (result.signal) {
|
|
|
|
|
process.kill(0, result.signal);
|
|
|
|
|
} else {
|
|
|
|
|
process.exit(result.status);
|
2018-12-06 16:52:33 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
const isWindows = process.platform === 'win32';
|
|
|
|
|
const isSunOS = process.platform === 'sunos';
|
|
|
|
|
const isFreeBSD = process.platform === 'freebsd';
|
|
|
|
|
const isOpenBSD = process.platform === 'openbsd';
|
|
|
|
|
const isLinux = process.platform === 'linux';
|
2024-07-15 21:32:26 +01:00
|
|
|
|
const isMacOS = process.platform === 'darwin';
|
2024-04-11 09:29:27 +02:00
|
|
|
|
const isASan = process.config.variables.asan === 1;
|
2024-08-28 20:17:19 +09:30
|
|
|
|
const isRiscv64 = process.arch === 'riscv64';
|
2024-08-13 14:37:02 +02:00
|
|
|
|
const isDebug = process.features.debug;
|
2025-02-10 17:17:47 -05:00
|
|
|
|
function isPi() {
|
2022-04-07 12:40:46 -04:00
|
|
|
|
try {
|
|
|
|
|
// Normal Raspberry Pi detection is to find the `Raspberry Pi` string in
|
|
|
|
|
// the contents of `/sys/firmware/devicetree/base/model` but that doesn't
|
|
|
|
|
// work inside a container. Match the chipset model number instead.
|
|
|
|
|
const cpuinfo = fs.readFileSync('/proc/cpuinfo', { encoding: 'utf8' });
|
2022-10-07 20:39:31 +02:00
|
|
|
|
const ok = /^Hardware\s*:\s*(.*)$/im.exec(cpuinfo)?.[1] === 'BCM2835';
|
|
|
|
|
/^/.test(''); // Clear RegExp.$_, some tests expect it to be empty.
|
|
|
|
|
return ok;
|
2022-04-07 12:40:46 -04:00
|
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-02-10 17:17:47 -05:00
|
|
|
|
}
|
2018-08-24 12:45:31 -07:00
|
|
|
|
|
2023-02-23 10:55:04 +01:00
|
|
|
|
// When using high concurrency or in the CI we need much more time for each connection attempt
|
2024-04-17 11:06:12 +02:00
|
|
|
|
net.setDefaultAutoSelectFamilyAttemptTimeout(platformTimeout(net.getDefaultAutoSelectFamilyAttemptTimeout() * 10));
|
|
|
|
|
const defaultAutoSelectFamilyAttemptTimeout = net.getDefaultAutoSelectFamilyAttemptTimeout();
|
2023-02-23 10:55:04 +01:00
|
|
|
|
|
2019-11-13 12:50:52 +00:00
|
|
|
|
const buildType = process.config.target_defaults ?
|
|
|
|
|
process.config.target_defaults.default_configuration :
|
|
|
|
|
'Release';
|
2015-11-06 12:51:48 -05:00
|
|
|
|
|
2017-03-09 16:13:34 -07:00
|
|
|
|
// If env var is set then enable async_hook hooks for all tests.
|
|
|
|
|
if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) {
|
|
|
|
|
const destroydIdsList = {};
|
|
|
|
|
const destroyListList = {};
|
|
|
|
|
const initHandles = {};
|
2018-08-21 08:54:02 +02:00
|
|
|
|
const { internalBinding } = require('internal/test/binding');
|
|
|
|
|
const async_wrap = internalBinding('async_wrap');
|
2017-03-09 16:13:34 -07:00
|
|
|
|
|
|
|
|
|
process.on('exit', () => {
|
2018-12-10 13:27:32 +01:00
|
|
|
|
// Iterate through handles to make sure nothing crashes
|
2017-03-09 16:13:34 -07:00
|
|
|
|
for (const k in initHandles)
|
2022-04-29 21:14:43 +02:00
|
|
|
|
inspect(initHandles[k]);
|
2017-03-09 16:13:34 -07:00
|
|
|
|
});
|
|
|
|
|
|
2017-09-26 15:50:10 +02:00
|
|
|
|
const _queueDestroyAsyncId = async_wrap.queueDestroyAsyncId;
|
|
|
|
|
async_wrap.queueDestroyAsyncId = function queueDestroyAsyncId(id) {
|
2017-03-09 16:13:34 -07:00
|
|
|
|
if (destroyListList[id] !== undefined) {
|
|
|
|
|
process._rawDebug(destroyListList[id]);
|
|
|
|
|
process._rawDebug();
|
2017-06-16 16:25:02 -06:00
|
|
|
|
throw new Error(`same id added to destroy list twice (${id})`);
|
2017-03-09 16:13:34 -07:00
|
|
|
|
}
|
2022-04-29 21:14:43 +02:00
|
|
|
|
destroyListList[id] = inspect(new Error());
|
2017-09-26 15:50:10 +02:00
|
|
|
|
_queueDestroyAsyncId(id);
|
2017-03-09 16:13:34 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
require('async_hooks').createHook({
|
2020-01-20 19:00:52 +01:00
|
|
|
|
init(id, ty, tr, resource) {
|
2017-03-09 16:13:34 -07:00
|
|
|
|
if (initHandles[id]) {
|
2017-06-16 16:25:02 -06:00
|
|
|
|
process._rawDebug(
|
2020-01-20 19:00:52 +01:00
|
|
|
|
`Is same resource: ${resource === initHandles[id].resource}`);
|
2017-06-16 16:25:02 -06:00
|
|
|
|
process._rawDebug(`Previous stack:\n${initHandles[id].stack}\n`);
|
2017-03-09 16:13:34 -07:00
|
|
|
|
throw new Error(`init called twice for same id (${id})`);
|
|
|
|
|
}
|
2020-01-20 19:00:52 +01:00
|
|
|
|
initHandles[id] = {
|
|
|
|
|
resource,
|
2024-05-23 21:45:18 +02:00
|
|
|
|
stack: inspect(new Error()).slice(6),
|
2020-01-20 19:00:52 +01:00
|
|
|
|
};
|
2017-03-09 16:13:34 -07:00
|
|
|
|
},
|
|
|
|
|
before() { },
|
|
|
|
|
after() { },
|
|
|
|
|
destroy(id) {
|
|
|
|
|
if (destroydIdsList[id] !== undefined) {
|
|
|
|
|
process._rawDebug(destroydIdsList[id]);
|
|
|
|
|
process._rawDebug();
|
|
|
|
|
throw new Error(`destroy called for same id (${id})`);
|
|
|
|
|
}
|
2022-04-29 21:14:43 +02:00
|
|
|
|
destroydIdsList[id] = inspect(new Error());
|
2017-03-09 16:13:34 -07:00
|
|
|
|
},
|
|
|
|
|
}).enable();
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-07 23:47:15 -06:00
|
|
|
|
let inFreeBSDJail = null;
|
|
|
|
|
let localhostIPv4 = null;
|
2015-02-09 13:56:38 +09:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
const localIPv6Hosts =
|
|
|
|
|
isLinux ? [
|
2016-02-27 22:18:22 -08:00
|
|
|
|
// Debian/Ubuntu
|
|
|
|
|
'ip6-localhost',
|
|
|
|
|
'ip6-loopback',
|
2015-12-17 01:16:46 -05:00
|
|
|
|
|
2016-02-27 22:18:22 -08:00
|
|
|
|
// SUSE
|
|
|
|
|
'ipv6-localhost',
|
|
|
|
|
'ipv6-loopback',
|
2015-12-17 01:16:46 -05:00
|
|
|
|
|
2016-02-27 22:18:22 -08:00
|
|
|
|
// Typically universal
|
|
|
|
|
'localhost',
|
2018-08-24 12:45:31 -07:00
|
|
|
|
] : [ 'localhost' ];
|
2015-03-17 12:06:48 +11:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
const PIPE = (() => {
|
2017-12-24 22:38:11 -08:00
|
|
|
|
const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`);
|
2018-08-24 12:45:31 -07:00
|
|
|
|
const pipePrefix = isWindows ? '\\\\.\\pipe\\' : localRelative;
|
2017-07-11 10:03:19 -04:00
|
|
|
|
const pipeName = `node-test.${process.pid}.sock`;
|
2018-08-24 12:45:31 -07:00
|
|
|
|
return path.join(pipePrefix, pipeName);
|
|
|
|
|
})();
|
2014-10-29 17:21:12 +01:00
|
|
|
|
|
2020-10-03 13:01:57 -07:00
|
|
|
|
// Check that when running a test with
|
|
|
|
|
// `$node --abort-on-uncaught-exception $file child`
|
|
|
|
|
// the process aborts.
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function childShouldThrowAndAbort() {
|
2024-09-29 22:44:52 +02:00
|
|
|
|
const escapedArgs = escapePOSIXShell`"${process.argv[0]}" --abort-on-uncaught-exception "${process.argv[1]}" child`;
|
2018-08-24 12:45:31 -07:00
|
|
|
|
if (!isWindows) {
|
2016-12-19 12:19:18 +08:00
|
|
|
|
// Do not create core files, as it can take a lot of disk space on
|
|
|
|
|
// continuous testing and developers' machines
|
2024-09-29 22:44:52 +02:00
|
|
|
|
escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0];
|
2016-12-19 12:19:18 +08:00
|
|
|
|
}
|
2025-01-22 16:58:49 -08:00
|
|
|
|
const { exec } = require('child_process');
|
2024-09-29 22:44:52 +02:00
|
|
|
|
const child = exec(...escapedArgs);
|
2016-12-19 12:19:18 +08:00
|
|
|
|
child.on('exit', function onExit(exitCode, signal) {
|
|
|
|
|
const errMsg = 'Test should have aborted ' +
|
|
|
|
|
`but instead exited with exit code ${exitCode}` +
|
|
|
|
|
` and signal ${signal}`;
|
2018-08-24 12:45:31 -07:00
|
|
|
|
assert(nodeProcessAborted(exitCode, signal), errMsg);
|
2016-12-19 12:19:18 +08:00
|
|
|
|
});
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2010-12-04 13:40:39 -08:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
const pwdCommand = isWindows ?
|
2018-08-25 12:23:53 -04:00
|
|
|
|
['cmd.exe', ['/d', '/c', 'cd']] :
|
|
|
|
|
['pwd', []];
|
2016-05-23 13:21:33 -04:00
|
|
|
|
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function platformTimeout(ms) {
|
2018-11-07 14:40:35 -08:00
|
|
|
|
const multipliers = typeof ms === 'bigint' ?
|
|
|
|
|
{ two: 2n, four: 4n, seven: 7n } : { two: 2, four: 4, seven: 7 };
|
|
|
|
|
|
2024-08-13 14:37:02 +02:00
|
|
|
|
if (isDebug)
|
2018-11-07 14:40:35 -08:00
|
|
|
|
ms = multipliers.two * ms;
|
2015-12-26 15:04:56 -08:00
|
|
|
|
|
2023-09-28 13:51:44 +01:00
|
|
|
|
if (exports.isAIX || exports.isIBMi)
|
2019-01-21 01:22:27 +01:00
|
|
|
|
return multipliers.two * ms; // Default localhost speed is slower on AIX
|
2016-04-21 18:20:37 -04:00
|
|
|
|
|
2025-02-10 17:17:47 -05:00
|
|
|
|
if (isPi())
|
2022-04-07 12:40:46 -04:00
|
|
|
|
return multipliers.two * ms; // Raspberry Pi devices
|
2015-12-10 15:03:59 -05:00
|
|
|
|
|
2024-08-28 20:17:19 +09:30
|
|
|
|
if (isRiscv64) {
|
|
|
|
|
return multipliers.four * ms;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 12:40:46 -04:00
|
|
|
|
return ms;
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2015-04-07 20:41:07 +02:00
|
|
|
|
|
2025-01-22 15:08:23 -08:00
|
|
|
|
const knownGlobals = new Set([
|
2024-05-18 04:50:47 -04:00
|
|
|
|
AbortController,
|
2021-03-17 13:11:05 -07:00
|
|
|
|
atob,
|
|
|
|
|
btoa,
|
2016-11-21 21:25:37 -08:00
|
|
|
|
clearImmediate,
|
|
|
|
|
clearInterval,
|
|
|
|
|
clearTimeout,
|
|
|
|
|
global,
|
|
|
|
|
setImmediate,
|
|
|
|
|
setInterval,
|
2019-01-20 10:25:15 -06:00
|
|
|
|
setTimeout,
|
|
|
|
|
queueMicrotask,
|
2025-01-22 15:08:23 -08:00
|
|
|
|
structuredClone,
|
|
|
|
|
fetch,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
['gc',
|
|
|
|
|
// The following are assumed to be conditionally available in the
|
|
|
|
|
// global object currently. They can likely be added to the fixed
|
|
|
|
|
// set of known globals, however.
|
|
|
|
|
'navigator',
|
|
|
|
|
'Navigator',
|
|
|
|
|
'performance',
|
|
|
|
|
'Performance',
|
|
|
|
|
'PerformanceMark',
|
|
|
|
|
'PerformanceMeasure',
|
|
|
|
|
'EventSource',
|
|
|
|
|
'CustomEvent',
|
|
|
|
|
'ReadableStream',
|
|
|
|
|
'ReadableStreamDefaultReader',
|
|
|
|
|
'ReadableStreamBYOBReader',
|
|
|
|
|
'ReadableStreamBYOBRequest',
|
|
|
|
|
'ReadableByteStreamController',
|
|
|
|
|
'ReadableStreamDefaultController',
|
|
|
|
|
'TransformStream',
|
|
|
|
|
'TransformStreamDefaultController',
|
|
|
|
|
'WritableStream',
|
|
|
|
|
'WritableStreamDefaultWriter',
|
|
|
|
|
'WritableStreamDefaultController',
|
|
|
|
|
'ByteLengthQueuingStrategy',
|
|
|
|
|
'CountQueuingStrategy',
|
|
|
|
|
'TextEncoderStream',
|
|
|
|
|
'TextDecoderStream',
|
|
|
|
|
'CompressionStream',
|
|
|
|
|
'DecompressionStream',
|
|
|
|
|
'Storage',
|
|
|
|
|
'localStorage',
|
|
|
|
|
'sessionStorage',
|
|
|
|
|
].forEach((i) => {
|
|
|
|
|
if (globalThis[i] !== undefined) {
|
|
|
|
|
knownGlobals.add(globalThis[i]);
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-02-01 09:17:50 +01:00
|
|
|
|
|
2025-01-22 15:08:23 -08:00
|
|
|
|
if (hasCrypto) {
|
|
|
|
|
knownGlobals.add(globalThis.crypto);
|
|
|
|
|
knownGlobals.add(globalThis.Crypto);
|
|
|
|
|
knownGlobals.add(globalThis.CryptoKey);
|
|
|
|
|
knownGlobals.add(globalThis.SubtleCrypto);
|
2023-09-15 10:32:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 10:07:47 +01:00
|
|
|
|
function allowGlobals(...allowlist) {
|
2025-01-22 15:08:23 -08:00
|
|
|
|
for (const val of allowlist) {
|
|
|
|
|
knownGlobals.add(val);
|
|
|
|
|
}
|
2016-07-21 12:44:01 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-15 09:31:10 -04:00
|
|
|
|
if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
|
|
|
|
|
if (process.env.NODE_TEST_KNOWN_GLOBALS) {
|
|
|
|
|
const knownFromEnv = process.env.NODE_TEST_KNOWN_GLOBALS.split(',');
|
|
|
|
|
allowGlobals(...knownFromEnv);
|
|
|
|
|
}
|
2011-08-13 23:51:31 +02:00
|
|
|
|
|
2019-04-15 09:31:10 -04:00
|
|
|
|
function leakedGlobals() {
|
|
|
|
|
const leaked = [];
|
|
|
|
|
|
2025-01-22 15:08:23 -08:00
|
|
|
|
for (const val in globalThis) {
|
2022-02-22 23:25:59 +01:00
|
|
|
|
// globalThis.crypto is a getter that throws if Node.js was compiled
|
2025-01-22 15:08:23 -08:00
|
|
|
|
// without OpenSSL so we'll skip it if it is not available.
|
|
|
|
|
if (val === 'crypto' && !hasCrypto) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!knownGlobals.has(globalThis[val])) {
|
2019-04-15 09:31:10 -04:00
|
|
|
|
leaked.push(val);
|
|
|
|
|
}
|
2017-06-20 14:37:00 -07:00
|
|
|
|
}
|
2019-04-15 09:31:10 -04:00
|
|
|
|
|
|
|
|
|
return leaked;
|
2017-06-20 14:37:00 -07:00
|
|
|
|
}
|
2010-12-04 13:40:39 -08:00
|
|
|
|
|
2019-04-15 09:31:10 -04:00
|
|
|
|
process.on('exit', function() {
|
|
|
|
|
const leaked = leakedGlobals();
|
|
|
|
|
if (leaked.length > 0) {
|
|
|
|
|
assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
2016-01-15 09:53:11 +01:00
|
|
|
|
}
|
2010-12-04 13:40:39 -08:00
|
|
|
|
|
2016-12-07 23:47:15 -06:00
|
|
|
|
const mustCallChecks = [];
|
2012-07-31 17:47:53 +02:00
|
|
|
|
|
2013-02-11 13:33:50 +01:00
|
|
|
|
function runCallChecks(exitCode) {
|
|
|
|
|
if (exitCode !== 0) return;
|
|
|
|
|
|
2016-12-07 23:47:15 -06:00
|
|
|
|
const failed = mustCallChecks.filter(function(context) {
|
2017-05-09 17:16:52 -04:00
|
|
|
|
if ('minimum' in context) {
|
|
|
|
|
context.messageSegment = `at least ${context.minimum}`;
|
|
|
|
|
return context.actual < context.minimum;
|
|
|
|
|
}
|
2020-04-08 18:58:03 +02:00
|
|
|
|
context.messageSegment = `exactly ${context.exact}`;
|
|
|
|
|
return context.actual !== context.exact;
|
2012-07-31 17:47:53 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
failed.forEach(function(context) {
|
2017-05-09 17:16:52 -04:00
|
|
|
|
console.log('Mismatched %s function calls. Expected %s, actual %d.',
|
2012-07-31 17:47:53 +02:00
|
|
|
|
context.name,
|
2017-05-09 17:16:52 -04:00
|
|
|
|
context.messageSegment,
|
2012-07-31 17:47:53 +02:00
|
|
|
|
context.actual);
|
|
|
|
|
console.log(context.stack.split('\n').slice(2).join('\n'));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (failed.length) process.exit(1);
|
|
|
|
|
}
|
2011-02-18 10:05:31 -08:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function mustCall(fn, exact) {
|
2017-05-09 17:16:52 -04:00
|
|
|
|
return _mustCallInner(fn, exact, 'exact');
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2012-07-31 17:47:53 +02:00
|
|
|
|
|
2020-09-06 22:27:07 +02:00
|
|
|
|
function mustSucceed(fn, exact) {
|
|
|
|
|
return mustCall(function(err, ...args) {
|
|
|
|
|
assert.ifError(err);
|
|
|
|
|
if (typeof fn === 'function')
|
|
|
|
|
return fn.apply(this, args);
|
|
|
|
|
}, exact);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function mustCallAtLeast(fn, minimum) {
|
2017-05-09 17:16:52 -04:00
|
|
|
|
return _mustCallInner(fn, minimum, 'minimum');
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-05-09 17:16:52 -04:00
|
|
|
|
|
2017-07-05 15:04:24 +02:00
|
|
|
|
function _mustCallInner(fn, criteria = 1, field) {
|
2017-12-04 05:46:40 -08:00
|
|
|
|
if (process._exiting)
|
|
|
|
|
throw new Error('Cannot use common.mustCall*() in process exit handler');
|
2017-03-24 09:46:44 -07:00
|
|
|
|
if (typeof fn === 'number') {
|
2017-05-09 17:16:52 -04:00
|
|
|
|
criteria = fn;
|
2017-03-24 09:46:44 -07:00
|
|
|
|
fn = noop;
|
|
|
|
|
} else if (fn === undefined) {
|
|
|
|
|
fn = noop;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-05 15:04:24 +02:00
|
|
|
|
if (typeof criteria !== 'number')
|
2017-05-09 17:16:52 -04:00
|
|
|
|
throw new TypeError(`Invalid ${field} value: ${criteria}`);
|
2012-07-31 17:47:53 +02:00
|
|
|
|
|
2016-12-07 23:47:15 -06:00
|
|
|
|
const context = {
|
2017-05-09 17:16:52 -04:00
|
|
|
|
[field]: criteria,
|
2012-07-31 17:47:53 +02:00
|
|
|
|
actual: 0,
|
2022-04-29 21:14:43 +02:00
|
|
|
|
stack: inspect(new Error()),
|
2022-11-21 12:38:12 -05:00
|
|
|
|
name: fn.name || '<anonymous>',
|
2012-07-31 17:47:53 +02:00
|
|
|
|
};
|
|
|
|
|
|
2018-12-03 17:15:45 +01:00
|
|
|
|
// Add the exit listener only once to avoid listener leak warnings
|
2012-07-31 17:47:53 +02:00
|
|
|
|
if (mustCallChecks.length === 0) process.on('exit', runCallChecks);
|
|
|
|
|
|
|
|
|
|
mustCallChecks.push(context);
|
|
|
|
|
|
2021-04-27 16:03:54 +02:00
|
|
|
|
const _return = function() { // eslint-disable-line func-style
|
2012-07-31 17:47:53 +02:00
|
|
|
|
context.actual++;
|
|
|
|
|
return fn.apply(this, arguments);
|
|
|
|
|
};
|
2021-04-27 16:03:54 +02:00
|
|
|
|
// Function instances have own properties that may be relevant.
|
|
|
|
|
// Let's replicate those properties to the returned function.
|
|
|
|
|
// Refs: https://tc39.es/ecma262/#sec-function-instances
|
|
|
|
|
Object.defineProperties(_return, {
|
|
|
|
|
name: {
|
|
|
|
|
value: fn.name,
|
|
|
|
|
writable: false,
|
|
|
|
|
enumerable: false,
|
|
|
|
|
configurable: true,
|
|
|
|
|
},
|
|
|
|
|
length: {
|
|
|
|
|
value: fn.length,
|
|
|
|
|
writable: false,
|
|
|
|
|
enumerable: false,
|
|
|
|
|
configurable: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
return _return;
|
2017-05-09 17:16:52 -04:00
|
|
|
|
}
|
2014-02-17 16:29:23 -08:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function skipIfEslintMissing() {
|
2018-08-06 06:47:11 -07:00
|
|
|
|
if (!fs.existsSync(
|
2024-06-19 21:54:08 +02:00
|
|
|
|
path.join(__dirname, '..', '..', 'tools', 'eslint', 'node_modules', 'eslint'),
|
2018-02-15 18:47:10 -05:00
|
|
|
|
)) {
|
2018-08-24 12:45:31 -07:00
|
|
|
|
skip('missing ESLint');
|
2018-02-15 18:47:10 -05:00
|
|
|
|
}
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2018-02-15 18:47:10 -05:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function canCreateSymLink() {
|
2016-12-27 15:18:41 -08:00
|
|
|
|
// On Windows, creating symlinks requires admin privileges.
|
|
|
|
|
// We'll only try to run symlink test if we have enough privileges.
|
|
|
|
|
// On other platforms, creating symlinks shouldn't need admin privileges
|
2018-08-24 12:45:31 -07:00
|
|
|
|
if (isWindows) {
|
2016-12-27 15:18:41 -08:00
|
|
|
|
// whoami.exe needs to be the one from System32
|
|
|
|
|
// If unix tools are in the path, they can shadow the one we want,
|
|
|
|
|
// so use the full path while executing whoami
|
2018-02-13 06:09:31 +01:00
|
|
|
|
const whoamiPath = path.join(process.env.SystemRoot,
|
2016-12-31 21:39:57 -08:00
|
|
|
|
'System32', 'whoami.exe');
|
2016-12-27 15:18:41 -08:00
|
|
|
|
|
|
|
|
|
try {
|
2025-01-22 16:58:49 -08:00
|
|
|
|
const { execSync } = require('child_process');
|
2019-08-20 17:24:39 -04:00
|
|
|
|
const output = execSync(`${whoamiPath} /priv`, { timeout: 1000 });
|
2018-02-13 03:01:46 +01:00
|
|
|
|
return output.includes('SeCreateSymbolicLinkPrivilege');
|
2018-11-04 10:25:02 -05:00
|
|
|
|
} catch {
|
2018-02-13 03:01:46 +01:00
|
|
|
|
return false;
|
2016-12-27 15:18:41 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-04 13:44:16 +09:00
|
|
|
|
// On non-Windows platforms, this always returns `true`
|
|
|
|
|
return true;
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2016-12-27 15:18:41 -08:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function mustNotCall(msg) {
|
2024-11-02 23:24:56 +08:00
|
|
|
|
const callSite = getCallSites()[1];
|
2020-06-18 23:30:47 +03:00
|
|
|
|
return function mustNotCall(...args) {
|
|
|
|
|
const argsInfo = args.length > 0 ?
|
2022-04-29 21:14:43 +02:00
|
|
|
|
`\ncalled with arguments: ${args.map((arg) => inspect(arg)).join(', ')}` : '';
|
2017-11-22 13:49:50 -05:00
|
|
|
|
assert.fail(
|
2024-09-14 17:27:04 -04:00
|
|
|
|
`${msg || 'function should not have been called'} at ${callSite.scriptName}:${callSite.lineNumber}` +
|
2020-06-18 23:30:47 +03:00
|
|
|
|
argsInfo);
|
2017-02-03 14:54:19 -05:00
|
|
|
|
};
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-02-03 14:54:19 -05:00
|
|
|
|
|
2022-07-13 01:56:08 +08:00
|
|
|
|
const _mustNotMutateObjectDeepProxies = new WeakMap();
|
|
|
|
|
|
|
|
|
|
function mustNotMutateObjectDeep(original) {
|
|
|
|
|
// Return primitives and functions directly. Primitives are immutable, and
|
|
|
|
|
// proxied functions are impossible to compare against originals, e.g. with
|
|
|
|
|
// `assert.deepEqual()`.
|
|
|
|
|
if (original === null || typeof original !== 'object') {
|
|
|
|
|
return original;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const cachedProxy = _mustNotMutateObjectDeepProxies.get(original);
|
|
|
|
|
if (cachedProxy) {
|
|
|
|
|
return cachedProxy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const _mustNotMutateObjectDeepHandler = {
|
|
|
|
|
__proto__: null,
|
|
|
|
|
defineProperty(target, property, descriptor) {
|
|
|
|
|
assert.fail(`Expected no side effects, got ${inspect(property)} ` +
|
|
|
|
|
'defined');
|
|
|
|
|
},
|
|
|
|
|
deleteProperty(target, property) {
|
|
|
|
|
assert.fail(`Expected no side effects, got ${inspect(property)} ` +
|
|
|
|
|
'deleted');
|
|
|
|
|
},
|
|
|
|
|
get(target, prop, receiver) {
|
|
|
|
|
return mustNotMutateObjectDeep(Reflect.get(target, prop, receiver));
|
|
|
|
|
},
|
|
|
|
|
preventExtensions(target) {
|
|
|
|
|
assert.fail('Expected no side effects, got extensions prevented on ' +
|
|
|
|
|
inspect(target));
|
|
|
|
|
},
|
|
|
|
|
set(target, property, value, receiver) {
|
|
|
|
|
assert.fail(`Expected no side effects, got ${inspect(value)} ` +
|
|
|
|
|
`assigned to ${inspect(property)}`);
|
|
|
|
|
},
|
|
|
|
|
setPrototypeOf(target, prototype) {
|
|
|
|
|
assert.fail(`Expected no side effects, got set prototype to ${prototype}`);
|
2022-11-21 12:38:12 -05:00
|
|
|
|
},
|
2022-07-13 01:56:08 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const proxy = new Proxy(original, _mustNotMutateObjectDeepHandler);
|
|
|
|
|
_mustNotMutateObjectDeepProxies.set(original, proxy);
|
|
|
|
|
return proxy;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function printSkipMessage(msg) {
|
2016-05-11 15:34:52 -04:00
|
|
|
|
console.log(`1..0 # Skipped: ${msg}`);
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2015-11-25 16:38:01 -05:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function skip(msg) {
|
|
|
|
|
printSkipMessage(msg);
|
2024-05-12 22:33:07 +02:00
|
|
|
|
// In known_issues test, skipping should produce a non-zero exit code.
|
|
|
|
|
process.exit(require.main?.filename.startsWith(path.resolve(__dirname, '../known_issues/')) ? 1 : 0);
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-07-01 02:29:09 +03:00
|
|
|
|
|
2015-11-02 17:56:24 -08:00
|
|
|
|
// Returns true if the exit code "exitCode" and/or signal name "signal"
|
|
|
|
|
// represent the exit code and/or signal name of a node process that aborted,
|
|
|
|
|
// false otherwise.
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function nodeProcessAborted(exitCode, signal) {
|
2015-11-02 17:56:24 -08:00
|
|
|
|
// Depending on the compiler used, node will exit with either
|
|
|
|
|
// exit code 132 (SIGILL), 133 (SIGTRAP) or 134 (SIGABRT).
|
2016-12-07 23:47:15 -06:00
|
|
|
|
let expectedExitCodes = [132, 133, 134];
|
2015-11-02 17:56:24 -08:00
|
|
|
|
|
|
|
|
|
// On platforms using KSH as the default shell (like SmartOS),
|
|
|
|
|
// when a process aborts, KSH exits with an exit code that is
|
|
|
|
|
// greater than 256, and thus the exit code emitted with the 'exit'
|
|
|
|
|
// event is null and the signal is set to either SIGILL, SIGTRAP,
|
|
|
|
|
// or SIGABRT (depending on the compiler).
|
|
|
|
|
const expectedSignals = ['SIGILL', 'SIGTRAP', 'SIGABRT'];
|
|
|
|
|
|
2017-05-05 07:25:36 -04:00
|
|
|
|
// On Windows, 'aborts' are of 2 types, depending on the context:
|
2021-05-19 18:20:56 +02:00
|
|
|
|
// (i) Exception breakpoint, if --abort-on-uncaught-exception is on
|
|
|
|
|
// which corresponds to exit code 2147483651 (0x80000003)
|
2017-06-24 15:37:59 -04:00
|
|
|
|
// (ii) Otherwise, _exit(134) which is called in place of abort() due to
|
|
|
|
|
// raising SIGABRT exiting with ambiguous exit code '3' by default
|
2018-08-24 12:45:31 -07:00
|
|
|
|
if (isWindows)
|
2021-05-19 18:20:56 +02:00
|
|
|
|
expectedExitCodes = [0x80000003, 134];
|
2015-11-02 17:56:24 -08:00
|
|
|
|
|
|
|
|
|
// When using --abort-on-uncaught-exception, V8 will use
|
|
|
|
|
// base::OS::Abort to terminate the process.
|
|
|
|
|
// Depending on the compiler used, the shell or other aspects of
|
|
|
|
|
// the platform used to build the node binary, this will actually
|
|
|
|
|
// make V8 exit by aborting or by raising a signal. In any case,
|
|
|
|
|
// one of them (exit code or signal) needs to be set to one of
|
|
|
|
|
// the expected exit codes or signals.
|
|
|
|
|
if (signal !== null) {
|
2016-11-21 21:25:37 -08:00
|
|
|
|
return expectedSignals.includes(signal);
|
2015-11-02 17:56:24 -08:00
|
|
|
|
}
|
2020-04-08 18:58:03 +02:00
|
|
|
|
return expectedExitCodes.includes(exitCode);
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2015-07-23 18:09:21 -06:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function isAlive(pid) {
|
2016-08-24 13:55:12 -07:00
|
|
|
|
try {
|
|
|
|
|
process.kill(pid, 'SIGCONT');
|
|
|
|
|
return true;
|
2018-11-04 10:25:02 -05:00
|
|
|
|
} catch {
|
2016-08-24 13:55:12 -07:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2018-03-20 12:39:46 +01:00
|
|
|
|
|
2018-12-28 14:33:33 +01:00
|
|
|
|
function _expectWarning(name, expected, code) {
|
|
|
|
|
if (typeof expected === 'string') {
|
|
|
|
|
expected = [[expected, code]];
|
|
|
|
|
} else if (!Array.isArray(expected)) {
|
|
|
|
|
expected = Object.entries(expected).map(([a, b]) => [b, a]);
|
2024-04-10 10:16:33 +02:00
|
|
|
|
} else if (expected.length !== 0 && !Array.isArray(expected[0])) {
|
2018-12-28 14:33:33 +01:00
|
|
|
|
expected = [[expected[0], expected[1]]];
|
|
|
|
|
}
|
|
|
|
|
// Deprecation codes are mandatory, everything else is not.
|
|
|
|
|
if (name === 'DeprecationWarning') {
|
2024-04-10 10:16:33 +02:00
|
|
|
|
expected.forEach(([_, code]) => assert(code, `Missing deprecation code: ${expected}`));
|
2018-12-28 14:33:33 +01:00
|
|
|
|
}
|
2018-08-24 12:45:31 -07:00
|
|
|
|
return mustCall((warning) => {
|
2021-12-29 21:58:53 -08:00
|
|
|
|
const expectedProperties = expected.shift();
|
|
|
|
|
if (!expectedProperties) {
|
|
|
|
|
assert.fail(`Unexpected extra warning received: ${warning}`);
|
|
|
|
|
}
|
|
|
|
|
const [ message, code ] = expectedProperties;
|
2016-09-17 18:25:03 +02:00
|
|
|
|
assert.strictEqual(warning.name, name);
|
2019-03-20 17:00:57 +01:00
|
|
|
|
if (typeof message === 'string') {
|
|
|
|
|
assert.strictEqual(warning.message, message);
|
|
|
|
|
} else {
|
2021-08-29 10:14:22 +02:00
|
|
|
|
assert.match(warning.message, message);
|
2019-03-20 17:00:57 +01:00
|
|
|
|
}
|
2018-05-31 17:21:59 +02:00
|
|
|
|
assert.strictEqual(warning.code, code);
|
2018-04-03 07:16:13 +02:00
|
|
|
|
}, expected.length);
|
2017-03-13 09:04:54 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-28 14:33:33 +01:00
|
|
|
|
let catchWarning;
|
2017-03-13 09:04:54 -07:00
|
|
|
|
|
2018-12-28 14:33:33 +01:00
|
|
|
|
// Accepts a warning name and description or array of descriptions or a map of
|
|
|
|
|
// warning names to description(s) ensures a warning is generated for each
|
|
|
|
|
// name/description pair.
|
|
|
|
|
// The expected messages have to be unique per `expectWarning()` call.
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function expectWarning(nameOrMap, expected, code) {
|
2018-12-28 14:33:33 +01:00
|
|
|
|
if (catchWarning === undefined) {
|
|
|
|
|
catchWarning = {};
|
2019-05-20 00:35:36 +02:00
|
|
|
|
process.on('warning', (warning) => {
|
|
|
|
|
if (!catchWarning[warning.name]) {
|
|
|
|
|
throw new TypeError(
|
|
|
|
|
`"${warning.name}" was triggered without being expected.\n` +
|
2022-11-21 12:38:12 -05:00
|
|
|
|
inspect(warning),
|
2019-05-20 00:35:36 +02:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
catchWarning[warning.name](warning);
|
|
|
|
|
});
|
2018-12-28 14:33:33 +01:00
|
|
|
|
}
|
2017-03-13 09:04:54 -07:00
|
|
|
|
if (typeof nameOrMap === 'string') {
|
2018-12-28 14:33:33 +01:00
|
|
|
|
catchWarning[nameOrMap] = _expectWarning(nameOrMap, expected, code);
|
2017-03-13 09:04:54 -07:00
|
|
|
|
} else {
|
2018-12-28 14:33:33 +01:00
|
|
|
|
Object.keys(nameOrMap).forEach((name) => {
|
|
|
|
|
catchWarning[name] = _expectWarning(name, nameOrMap[name]);
|
|
|
|
|
});
|
2017-03-13 09:04:54 -07:00
|
|
|
|
}
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-06-15 14:51:05 +02:00
|
|
|
|
|
2016-10-24 13:09:34 -07:00
|
|
|
|
// Useful for testing expected internal/error objects
|
2019-12-25 18:02:16 +01:00
|
|
|
|
function expectsError(validator, exact) {
|
|
|
|
|
return mustCall((...args) => {
|
|
|
|
|
if (args.length !== 1) {
|
2020-01-20 19:00:52 +01:00
|
|
|
|
// Do not use `assert.strictEqual()` to prevent `inspect` from
|
2018-04-26 02:14:25 +02:00
|
|
|
|
// always being called.
|
2022-04-29 21:14:43 +02:00
|
|
|
|
assert.fail(`Expected one argument, got ${inspect(args)}`);
|
2018-04-26 02:14:25 +02:00
|
|
|
|
}
|
2019-12-25 18:02:16 +01:00
|
|
|
|
const error = args.pop();
|
2018-08-07 11:40:38 +01:00
|
|
|
|
// The error message should be non-enumerable
|
2023-09-28 11:57:38 +02:00
|
|
|
|
assert.strictEqual(Object.prototype.propertyIsEnumerable.call(error, 'message'), false);
|
2018-04-04 17:03:58 +02:00
|
|
|
|
|
2019-12-25 18:02:16 +01:00
|
|
|
|
assert.throws(() => { throw error; }, validator);
|
2016-10-24 13:09:34 -07:00
|
|
|
|
return true;
|
2019-12-25 18:02:16 +01:00
|
|
|
|
}, exact);
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-03-01 08:16:48 +01:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function skipIfInspectorDisabled() {
|
2019-01-30 23:19:45 +08:00
|
|
|
|
if (!process.features.inspector) {
|
2018-08-24 12:45:31 -07:00
|
|
|
|
skip('V8 inspector is disabled');
|
2017-03-01 08:16:48 +01:00
|
|
|
|
}
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-04-03 18:29:41 -07:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function skipIf32Bits() {
|
|
|
|
|
if (bits < 64) {
|
|
|
|
|
skip('The tested feature is not available in 32bit builds');
|
2017-07-17 16:51:26 +02:00
|
|
|
|
}
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-07-17 16:51:26 +02:00
|
|
|
|
|
2025-04-29 22:38:51 +00:00
|
|
|
|
function skipIfSQLiteMissing() {
|
|
|
|
|
if (!hasSQLite) {
|
|
|
|
|
skip('missing SQLite');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function getArrayBufferViews(buf) {
|
2017-04-03 18:29:41 -07:00
|
|
|
|
const { buffer, byteOffset, byteLength } = buf;
|
|
|
|
|
|
|
|
|
|
const out = [];
|
2017-12-22 15:29:13 -08:00
|
|
|
|
|
|
|
|
|
const arrayBufferViews = [
|
|
|
|
|
Int8Array,
|
|
|
|
|
Uint8Array,
|
|
|
|
|
Uint8ClampedArray,
|
|
|
|
|
Int16Array,
|
|
|
|
|
Uint16Array,
|
|
|
|
|
Int32Array,
|
|
|
|
|
Uint32Array,
|
|
|
|
|
Float32Array,
|
|
|
|
|
Float64Array,
|
2022-06-02 06:07:33 +08:00
|
|
|
|
BigInt64Array,
|
|
|
|
|
BigUint64Array,
|
2021-03-26 08:51:08 -07:00
|
|
|
|
DataView,
|
2017-12-22 15:29:13 -08:00
|
|
|
|
];
|
|
|
|
|
|
2017-04-03 18:29:41 -07:00
|
|
|
|
for (const type of arrayBufferViews) {
|
|
|
|
|
const { BYTES_PER_ELEMENT = 1 } = type;
|
|
|
|
|
if (byteLength % BYTES_PER_ELEMENT === 0) {
|
|
|
|
|
out.push(new type(buffer, byteOffset, byteLength / BYTES_PER_ELEMENT));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return out;
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2017-04-19 19:17:31 +02:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function getBufferSources(buf) {
|
|
|
|
|
return [...getArrayBufferViews(buf), new Uint8Array(buf).buffer];
|
|
|
|
|
}
|
2017-10-06 12:19:33 -07:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function getTTYfd() {
|
2018-02-16 12:09:54 -05:00
|
|
|
|
// Do our best to grab a tty fd.
|
|
|
|
|
const tty = require('tty');
|
|
|
|
|
// Don't attempt fd 0 as it is not writable on Windows.
|
|
|
|
|
// Ref: ef2861961c3d9e9ed6972e1e84d969683b25cf95
|
|
|
|
|
const ttyFd = [1, 2, 4, 5].find(tty.isatty);
|
|
|
|
|
if (ttyFd === undefined) {
|
|
|
|
|
try {
|
|
|
|
|
return fs.openSync('/dev/tty');
|
2018-11-04 10:25:02 -05:00
|
|
|
|
} catch {
|
2018-02-16 12:09:54 -05:00
|
|
|
|
// There aren't any tty fd's available to use.
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ttyFd;
|
2018-08-24 12:45:31 -07:00
|
|
|
|
}
|
2018-02-16 12:09:54 -05:00
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
function runWithInvalidFD(func) {
|
2018-02-19 17:36:59 +08:00
|
|
|
|
let fd = 1 << 30;
|
|
|
|
|
// Get first known bad file descriptor. 1 << 30 is usually unlikely to
|
|
|
|
|
// be an valid one.
|
|
|
|
|
try {
|
|
|
|
|
while (fs.fstatSync(fd--) && fd > 0);
|
2018-11-04 10:25:02 -05:00
|
|
|
|
} catch {
|
2018-02-19 17:36:59 +08:00
|
|
|
|
return func(fd);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
printSkipMessage('Could not generate an invalid fd');
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-23 08:17:25 +02:00
|
|
|
|
// A helper function to simplify checking for ERR_INVALID_ARG_TYPE output.
|
|
|
|
|
function invalidArgTypeHelper(input) {
|
|
|
|
|
if (input == null) {
|
|
|
|
|
return ` Received ${input}`;
|
|
|
|
|
}
|
2023-09-30 23:43:40 +02:00
|
|
|
|
if (typeof input === 'function') {
|
2019-09-23 08:17:25 +02:00
|
|
|
|
return ` Received function ${input.name}`;
|
|
|
|
|
}
|
|
|
|
|
if (typeof input === 'object') {
|
2022-07-01 20:08:08 +02:00
|
|
|
|
if (input.constructor?.name) {
|
2019-09-23 08:17:25 +02:00
|
|
|
|
return ` Received an instance of ${input.constructor.name}`;
|
|
|
|
|
}
|
2022-04-29 21:14:43 +02:00
|
|
|
|
return ` Received ${inspect(input, { depth: -1 })}`;
|
2019-09-23 08:17:25 +02:00
|
|
|
|
}
|
2022-07-01 20:08:08 +02:00
|
|
|
|
|
2022-04-29 21:14:43 +02:00
|
|
|
|
let inspected = inspect(input, { colors: false });
|
2022-07-01 20:08:08 +02:00
|
|
|
|
if (inspected.length > 28) { inspected = `${inspected.slice(inspected, 0, 25)}...`; }
|
|
|
|
|
|
2019-09-23 08:17:25 +02:00
|
|
|
|
return ` Received type ${typeof input} (${inspected})`;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-18 14:56:24 -04:00
|
|
|
|
function requireNoPackageJSONAbove(dir = __dirname) {
|
|
|
|
|
let possiblePackage = path.join(dir, '..', 'package.json');
|
2020-10-13 14:21:33 -05:00
|
|
|
|
let lastPackage = null;
|
|
|
|
|
while (possiblePackage !== lastPackage) {
|
|
|
|
|
if (fs.existsSync(possiblePackage)) {
|
|
|
|
|
assert.fail(
|
|
|
|
|
'This test shouldn\'t load properties from a package.json above ' +
|
|
|
|
|
`its file location. Found package.json at ${possiblePackage}.`);
|
|
|
|
|
}
|
|
|
|
|
lastPackage = possiblePackage;
|
|
|
|
|
possiblePackage = path.join(possiblePackage, '..', '..', 'package.json');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 10:42:55 +02:00
|
|
|
|
function spawnPromisified(...args) {
|
2025-01-22 16:58:49 -08:00
|
|
|
|
const { spawn } = require('child_process');
|
2022-07-29 10:42:55 +02:00
|
|
|
|
let stderr = '';
|
|
|
|
|
let stdout = '';
|
|
|
|
|
|
|
|
|
|
const child = spawn(...args);
|
|
|
|
|
child.stderr.setEncoding('utf8');
|
|
|
|
|
child.stderr.on('data', (data) => { stderr += data; });
|
|
|
|
|
child.stdout.setEncoding('utf8');
|
|
|
|
|
child.stdout.on('data', (data) => { stdout += data; });
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
child.on('close', (code, signal) => {
|
|
|
|
|
resolve({
|
|
|
|
|
code,
|
|
|
|
|
signal,
|
|
|
|
|
stderr,
|
|
|
|
|
stdout,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
child.on('error', (code, signal) => {
|
|
|
|
|
reject({
|
|
|
|
|
code,
|
|
|
|
|
signal,
|
|
|
|
|
stderr,
|
|
|
|
|
stdout,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 22:44:52 +02:00
|
|
|
|
/**
|
|
|
|
|
* Escape values in a string template literal. On Windows, this function
|
|
|
|
|
* does not escape anything (which is fine for paths, as `"` is not a valid char
|
|
|
|
|
* in a path on Windows), so you should use it only to escape paths – or other
|
|
|
|
|
* values on tests which are skipped on Windows.
|
|
|
|
|
* This function is meant to be used for tagged template strings.
|
|
|
|
|
* @returns {[string, object | undefined]} An array that can be passed as
|
|
|
|
|
* arguments to `exec` or `execSync`.
|
|
|
|
|
*/
|
|
|
|
|
function escapePOSIXShell(cmdParts, ...args) {
|
|
|
|
|
if (common.isWindows) {
|
|
|
|
|
// On Windows, paths cannot contain `"`, so we can return the string unchanged.
|
|
|
|
|
return [String.raw({ raw: cmdParts }, ...args)];
|
|
|
|
|
}
|
|
|
|
|
// On POSIX shells, we can pass values via the env, as there's a standard way for referencing a variable.
|
|
|
|
|
const env = { ...process.env };
|
|
|
|
|
let cmd = cmdParts[0];
|
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
|
|
|
const envVarName = `ESCAPED_${i}`;
|
|
|
|
|
env[envVarName] = args[i];
|
|
|
|
|
cmd += '${' + envVarName + '}' + cmdParts[i + 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [cmd, { env }];
|
|
|
|
|
};
|
|
|
|
|
|
2024-05-07 08:11:54 +08:00
|
|
|
|
/**
|
|
|
|
|
* Check the exports of require(esm).
|
|
|
|
|
* TODO(joyeecheung): use it in all the test-require-module-* tests to minimize changes
|
|
|
|
|
* if/when we change the layout of the result returned by require(esm).
|
|
|
|
|
* @param {object} mod result returned by require()
|
|
|
|
|
* @param {object} expectation shape of expected namespace.
|
|
|
|
|
*/
|
2024-03-20 21:41:22 +01:00
|
|
|
|
function expectRequiredModule(mod, expectation, checkESModule = true) {
|
2025-01-22 16:58:49 -08:00
|
|
|
|
const { isModuleNamespaceObject } = require('util/types');
|
2024-03-20 21:41:22 +01:00
|
|
|
|
const clone = { ...mod };
|
|
|
|
|
if (Object.hasOwn(mod, 'default') && checkESModule) {
|
|
|
|
|
assert.strictEqual(mod.__esModule, true);
|
|
|
|
|
delete clone.__esModule;
|
|
|
|
|
}
|
2024-05-07 08:11:54 +08:00
|
|
|
|
assert(isModuleNamespaceObject(mod));
|
2024-03-20 21:41:22 +01:00
|
|
|
|
assert.deepStrictEqual(clone, { ...expectation });
|
2024-05-07 08:11:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-17 19:44:50 +01:00
|
|
|
|
function expectRequiredTLAError(err) {
|
|
|
|
|
const message = /require\(\) cannot be used on an ESM graph with top-level await/;
|
|
|
|
|
if (typeof err === 'string') {
|
|
|
|
|
assert.match(err, /ERR_REQUIRE_ASYNC_MODULE/);
|
|
|
|
|
assert.match(err, message);
|
|
|
|
|
} else {
|
|
|
|
|
assert.strictEqual(err.code, 'ERR_REQUIRE_ASYNC_MODULE');
|
|
|
|
|
assert.match(err.message, message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-24 14:55:14 +02:00
|
|
|
|
const common = {
|
2018-08-24 12:45:31 -07:00
|
|
|
|
allowGlobals,
|
|
|
|
|
buildType,
|
|
|
|
|
canCreateSymLink,
|
|
|
|
|
childShouldThrowAndAbort,
|
2023-02-23 10:55:04 +01:00
|
|
|
|
defaultAutoSelectFamilyAttemptTimeout,
|
2024-09-29 22:44:52 +02:00
|
|
|
|
escapePOSIXShell,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
expectsError,
|
2024-05-07 08:11:54 +08:00
|
|
|
|
expectRequiredModule,
|
2025-02-17 19:44:50 +01:00
|
|
|
|
expectRequiredTLAError,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
expectWarning,
|
|
|
|
|
getArrayBufferViews,
|
|
|
|
|
getBufferSources,
|
|
|
|
|
getTTYfd,
|
|
|
|
|
hasIntl,
|
|
|
|
|
hasCrypto,
|
2021-03-09 13:50:08 -08:00
|
|
|
|
hasQuic,
|
2025-04-29 22:38:51 +00:00
|
|
|
|
hasSQLite,
|
2019-09-23 08:17:25 +02:00
|
|
|
|
invalidArgTypeHelper,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
isAlive,
|
2024-04-11 09:29:27 +02:00
|
|
|
|
isASan,
|
2024-08-13 14:37:02 +02:00
|
|
|
|
isDebug,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
isFreeBSD,
|
|
|
|
|
isLinux,
|
|
|
|
|
isOpenBSD,
|
2024-07-15 21:32:26 +01:00
|
|
|
|
isMacOS,
|
2022-04-07 12:40:46 -04:00
|
|
|
|
isPi,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
isSunOS,
|
|
|
|
|
isWindows,
|
|
|
|
|
localIPv6Hosts,
|
|
|
|
|
mustCall,
|
|
|
|
|
mustCallAtLeast,
|
|
|
|
|
mustNotCall,
|
2022-07-13 01:56:08 +08:00
|
|
|
|
mustNotMutateObjectDeep,
|
2020-09-06 22:27:07 +02:00
|
|
|
|
mustSucceed,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
nodeProcessAborted,
|
|
|
|
|
PIPE,
|
2023-04-04 00:41:13 +03:00
|
|
|
|
parseTestFlags,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
platformTimeout,
|
|
|
|
|
printSkipMessage,
|
|
|
|
|
pwdCommand,
|
2020-10-13 14:21:33 -05:00
|
|
|
|
requireNoPackageJSONAbove,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
runWithInvalidFD,
|
|
|
|
|
skip,
|
|
|
|
|
skipIf32Bits,
|
|
|
|
|
skipIfEslintMissing,
|
|
|
|
|
skipIfInspectorDisabled,
|
2025-04-29 22:38:51 +00:00
|
|
|
|
skipIfSQLiteMissing,
|
2022-07-29 10:42:55 +02:00
|
|
|
|
spawnPromisified,
|
2018-08-24 12:45:31 -07:00
|
|
|
|
|
2019-12-20 06:51:42 -08:00
|
|
|
|
get enoughTestMem() {
|
2019-12-12 10:42:52 -05:00
|
|
|
|
return require('os').totalmem() > 0x70000000; /* 1.75 Gb */
|
|
|
|
|
},
|
2018-08-24 12:45:31 -07:00
|
|
|
|
|
2019-12-12 10:42:52 -05:00
|
|
|
|
get hasIPv6() {
|
|
|
|
|
const iFaces = require('os').networkInterfaces();
|
2023-02-23 08:44:58 -06:00
|
|
|
|
let re;
|
|
|
|
|
if (isWindows) {
|
|
|
|
|
re = /Loopback Pseudo-Interface/;
|
|
|
|
|
} else if (this.isIBMi) {
|
|
|
|
|
re = /\*LOOPBACK/;
|
|
|
|
|
} else {
|
|
|
|
|
re = /lo/;
|
|
|
|
|
}
|
2019-12-12 10:42:52 -05:00
|
|
|
|
return Object.keys(iFaces).some((name) => {
|
|
|
|
|
return re.test(name) &&
|
2022-05-10 11:10:03 +02:00
|
|
|
|
iFaces[name].some(({ family }) => family === 'IPv6');
|
2019-12-12 10:42:52 -05:00
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
get inFreeBSDJail() {
|
2025-01-22 16:58:49 -08:00
|
|
|
|
const { execSync } = require('child_process');
|
2018-08-24 12:45:31 -07:00
|
|
|
|
if (inFreeBSDJail !== null) return inFreeBSDJail;
|
|
|
|
|
|
|
|
|
|
if (exports.isFreeBSD &&
|
|
|
|
|
execSync('sysctl -n security.jail.jailed').toString() === '1\n') {
|
|
|
|
|
inFreeBSDJail = true;
|
|
|
|
|
} else {
|
|
|
|
|
inFreeBSDJail = false;
|
|
|
|
|
}
|
|
|
|
|
return inFreeBSDJail;
|
|
|
|
|
},
|
|
|
|
|
|
2019-12-12 10:42:52 -05:00
|
|
|
|
// On IBMi, process.platform and os.platform() both return 'aix',
|
2023-09-28 13:51:44 +01:00
|
|
|
|
// when built with Python versions earlier than 3.9.
|
2019-12-12 10:42:52 -05:00
|
|
|
|
// It is not enough to differentiate between IBMi and real AIX system.
|
2023-09-28 13:51:44 +01:00
|
|
|
|
get isAIX() {
|
|
|
|
|
return require('os').type() === 'AIX';
|
|
|
|
|
},
|
|
|
|
|
|
2019-12-12 10:42:52 -05:00
|
|
|
|
get isIBMi() {
|
|
|
|
|
return require('os').type() === 'OS400';
|
|
|
|
|
},
|
|
|
|
|
|
2018-08-24 12:45:31 -07:00
|
|
|
|
get localhostIPv4() {
|
|
|
|
|
if (localhostIPv4 !== null) return localhostIPv4;
|
|
|
|
|
|
|
|
|
|
if (this.inFreeBSDJail) {
|
|
|
|
|
// Jailed network interfaces are a bit special - since we need to jump
|
|
|
|
|
// through loops, as well as this being an exception case, assume the
|
|
|
|
|
// user will provide this instead.
|
|
|
|
|
if (process.env.LOCALHOST) {
|
|
|
|
|
localhostIPv4 = process.env.LOCALHOST;
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Looks like we\'re in a FreeBSD Jail. ' +
|
|
|
|
|
'Please provide your default interface address ' +
|
|
|
|
|
'as LOCALHOST or expect some tests to fail.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1';
|
|
|
|
|
|
|
|
|
|
return localhostIPv4;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
get PORT() {
|
|
|
|
|
if (+process.env.TEST_PARALLEL) {
|
|
|
|
|
throw new Error('common.PORT cannot be used in a parallelized test');
|
|
|
|
|
}
|
|
|
|
|
return +process.env.NODE_COMMON_PORT || 12346;
|
2021-09-05 12:57:30 +02:00
|
|
|
|
},
|
2018-08-24 12:45:31 -07:00
|
|
|
|
|
2024-12-30 01:17:44 +01:00
|
|
|
|
get isInsideDirWithUnusualChars() {
|
|
|
|
|
return __dirname.includes('%') ||
|
|
|
|
|
(!isWindows && __dirname.includes('\\')) ||
|
|
|
|
|
__dirname.includes('$') ||
|
|
|
|
|
__dirname.includes('\n') ||
|
|
|
|
|
__dirname.includes('\r') ||
|
|
|
|
|
__dirname.includes('\t');
|
|
|
|
|
},
|
2018-02-19 17:36:59 +08:00
|
|
|
|
};
|
2020-02-24 14:55:14 +02:00
|
|
|
|
|
|
|
|
|
const validProperties = new Set(Object.keys(common));
|
|
|
|
|
module.exports = new Proxy(common, {
|
|
|
|
|
get(obj, prop) {
|
|
|
|
|
if (!validProperties.has(prop))
|
|
|
|
|
throw new Error(`Using invalid common property: '${prop}'`);
|
|
|
|
|
return obj[prop];
|
2022-11-21 12:38:12 -05:00
|
|
|
|
},
|
2020-02-24 14:55:14 +02:00
|
|
|
|
});
|