1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22/* eslint-disable node-core/crypto-check */ 23'use strict'; 24const process = global.process; // Some tests tamper with the process global. 25 26const assert = require('assert'); 27const { exec, execSync, spawn, spawnSync } = require('child_process'); 28const fs = require('fs'); 29// Do not require 'os' until needed so that test-os-checked-function can 30// monkey patch it. If 'os' is required here, that test will fail. 31const path = require('path'); 32const { inspect } = require('util'); 33const { isMainThread } = require('worker_threads'); 34 35const tmpdir = require('./tmpdir'); 36const bits = ['arm64', 'loong64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x', 'x64'] 37 .includes(process.arch) ? 64 : 32; 38const hasIntl = !!process.config.variables.v8_enable_i18n_support; 39 40const { 41 atob, 42 btoa, 43} = require('buffer'); 44 45// Some tests assume a umask of 0o022 so set that up front. Tests that need a 46// different umask will set it themselves. 47// 48// Workers can read, but not set the umask, so check that this is the main 49// thread. 50if (isMainThread) 51 process.umask(0o022); 52 53const noop = () => {}; 54 55const hasCrypto = Boolean(process.versions.openssl) && 56 !process.env.NODE_SKIP_CRYPTO; 57 58const hasOpenSSL3 = hasCrypto && 59 require('crypto').constants.OPENSSL_VERSION_NUMBER >= 0x30000000; 60 61const hasOpenSSL31 = hasCrypto && 62 require('crypto').constants.OPENSSL_VERSION_NUMBER >= 0x30100000; 63 64const hasQuic = hasCrypto && !!process.config.variables.openssl_quic; 65 66function parseTestFlags(filename = process.argv[1]) { 67 // The copyright notice is relatively big and the flags could come afterwards. 68 const bytesToRead = 1500; 69 const buffer = Buffer.allocUnsafe(bytesToRead); 70 const fd = fs.openSync(filename, 'r'); 71 const bytesRead = fs.readSync(fd, buffer, 0, bytesToRead); 72 fs.closeSync(fd); 73 const source = buffer.toString('utf8', 0, bytesRead); 74 75 const flagStart = source.search(/\/\/ Flags:\s+--/) + 10; 76 77 if (flagStart === 9) { 78 return []; 79 } 80 let flagEnd = source.indexOf('\n', flagStart); 81 // Normalize different EOL. 82 if (source[flagEnd - 1] === '\r') { 83 flagEnd--; 84 } 85 return source 86 .substring(flagStart, flagEnd) 87 .split(/\s+/) 88 .filter(Boolean); 89} 90 91// Check for flags. Skip this for workers (both, the `cluster` module and 92// `worker_threads`) and child processes. 93// If the binary was built without-ssl then the crypto flags are 94// invalid (bad option). The test itself should handle this case. 95if (process.argv.length === 2 && 96 !process.env.NODE_SKIP_FLAG_CHECK && 97 isMainThread && 98 hasCrypto && 99 require('cluster').isPrimary && 100 fs.existsSync(process.argv[1])) { 101 const flags = parseTestFlags(); 102 for (const flag of flags) { 103 if (!process.execArgv.includes(flag) && 104 // If the binary is build without `intl` the inspect option is 105 // invalid. The test itself should handle this case. 106 (process.features.inspector || !flag.startsWith('--inspect'))) { 107 console.log( 108 'NOTE: The test started as a child_process using these flags:', 109 inspect(flags), 110 'Use NODE_SKIP_FLAG_CHECK to run the test with the original flags.', 111 ); 112 const args = [...flags, ...process.execArgv, ...process.argv.slice(1)]; 113 const options = { encoding: 'utf8', stdio: 'inherit' }; 114 const result = spawnSync(process.execPath, args, options); 115 if (result.signal) { 116 process.kill(0, result.signal); 117 } else { 118 process.exit(result.status); 119 } 120 } 121 } 122} 123 124const isWindows = process.platform === 'win32'; 125const isSunOS = process.platform === 'sunos'; 126const isFreeBSD = process.platform === 'freebsd'; 127const isOpenBSD = process.platform === 'openbsd'; 128const isLinux = process.platform === 'linux'; 129const isOSX = process.platform === 'darwin'; 130const isAsan = process.env.ASAN !== undefined; 131const isPi = (() => { 132 try { 133 // Normal Raspberry Pi detection is to find the `Raspberry Pi` string in 134 // the contents of `/sys/firmware/devicetree/base/model` but that doesn't 135 // work inside a container. Match the chipset model number instead. 136 const cpuinfo = fs.readFileSync('/proc/cpuinfo', { encoding: 'utf8' }); 137 const ok = /^Hardware\s*:\s*(.*)$/im.exec(cpuinfo)?.[1] === 'BCM2835'; 138 /^/.test(''); // Clear RegExp.$_, some tests expect it to be empty. 139 return ok; 140 } catch { 141 return false; 142 } 143})(); 144 145const isDumbTerminal = process.env.TERM === 'dumb'; 146 147const buildType = process.config.target_defaults ? 148 process.config.target_defaults.default_configuration : 149 'Release'; 150 151// If env var is set then enable async_hook hooks for all tests. 152if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) { 153 const destroydIdsList = {}; 154 const destroyListList = {}; 155 const initHandles = {}; 156 const { internalBinding } = require('internal/test/binding'); 157 const async_wrap = internalBinding('async_wrap'); 158 159 process.on('exit', () => { 160 // Iterate through handles to make sure nothing crashes 161 for (const k in initHandles) 162 inspect(initHandles[k]); 163 }); 164 165 const _queueDestroyAsyncId = async_wrap.queueDestroyAsyncId; 166 async_wrap.queueDestroyAsyncId = function queueDestroyAsyncId(id) { 167 if (destroyListList[id] !== undefined) { 168 process._rawDebug(destroyListList[id]); 169 process._rawDebug(); 170 throw new Error(`same id added to destroy list twice (${id})`); 171 } 172 destroyListList[id] = inspect(new Error()); 173 _queueDestroyAsyncId(id); 174 }; 175 176 require('async_hooks').createHook({ 177 init(id, ty, tr, resource) { 178 if (initHandles[id]) { 179 process._rawDebug( 180 `Is same resource: ${resource === initHandles[id].resource}`); 181 process._rawDebug(`Previous stack:\n${initHandles[id].stack}\n`); 182 throw new Error(`init called twice for same id (${id})`); 183 } 184 initHandles[id] = { 185 resource, 186 stack: inspect(new Error()).substr(6), 187 }; 188 }, 189 before() { }, 190 after() { }, 191 destroy(id) { 192 if (destroydIdsList[id] !== undefined) { 193 process._rawDebug(destroydIdsList[id]); 194 process._rawDebug(); 195 throw new Error(`destroy called for same id (${id})`); 196 } 197 destroydIdsList[id] = inspect(new Error()); 198 }, 199 }).enable(); 200} 201 202let opensslCli = null; 203let inFreeBSDJail = null; 204let localhostIPv4 = null; 205 206const localIPv6Hosts = 207 isLinux ? [ 208 // Debian/Ubuntu 209 'ip6-localhost', 210 'ip6-loopback', 211 212 // SUSE 213 'ipv6-localhost', 214 'ipv6-loopback', 215 216 // Typically universal 217 'localhost', 218 ] : [ 'localhost' ]; 219 220const PIPE = (() => { 221 const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`); 222 const pipePrefix = isWindows ? '\\\\.\\pipe\\' : localRelative; 223 const pipeName = `node-test.${process.pid}.sock`; 224 return path.join(pipePrefix, pipeName); 225})(); 226 227// Check that when running a test with 228// `$node --abort-on-uncaught-exception $file child` 229// the process aborts. 230function childShouldThrowAndAbort() { 231 let testCmd = ''; 232 if (!isWindows) { 233 // Do not create core files, as it can take a lot of disk space on 234 // continuous testing and developers' machines 235 testCmd += 'ulimit -c 0 && '; 236 } 237 testCmd += `"${process.argv[0]}" --abort-on-uncaught-exception `; 238 testCmd += `"${process.argv[1]}" child`; 239 const child = exec(testCmd); 240 child.on('exit', function onExit(exitCode, signal) { 241 const errMsg = 'Test should have aborted ' + 242 `but instead exited with exit code ${exitCode}` + 243 ` and signal ${signal}`; 244 assert(nodeProcessAborted(exitCode, signal), errMsg); 245 }); 246} 247 248function createZeroFilledFile(filename) { 249 const fd = fs.openSync(filename, 'w'); 250 fs.ftruncateSync(fd, 10 * 1024 * 1024); 251 fs.closeSync(fd); 252} 253 254 255const pwdCommand = isWindows ? 256 ['cmd.exe', ['/d', '/c', 'cd']] : 257 ['pwd', []]; 258 259 260function platformTimeout(ms) { 261 const multipliers = typeof ms === 'bigint' ? 262 { two: 2n, four: 4n, seven: 7n } : { two: 2, four: 4, seven: 7 }; 263 264 if (process.features.debug) 265 ms = multipliers.two * ms; 266 267 if (exports.isAIX || exports.isIBMi) 268 return multipliers.two * ms; // Default localhost speed is slower on AIX 269 270 if (isPi) 271 return multipliers.two * ms; // Raspberry Pi devices 272 273 return ms; 274} 275 276let knownGlobals = [ 277 atob, 278 btoa, 279 clearImmediate, 280 clearInterval, 281 clearTimeout, 282 global, 283 setImmediate, 284 setInterval, 285 setTimeout, 286 queueMicrotask, 287]; 288 289// TODO(@jasnell): This check can be temporary. AbortController is 290// not currently supported in either Node.js 12 or 10, making it 291// difficult to run tests comparatively on those versions. Once 292// all supported versions have AbortController as a global, this 293// check can be removed and AbortController can be added to the 294// knownGlobals list above. 295if (global.AbortController) 296 knownGlobals.push(global.AbortController); 297 298if (global.gc) { 299 knownGlobals.push(global.gc); 300} 301 302if (global.Performance) { 303 knownGlobals.push(global.Performance); 304} 305if (global.performance) { 306 knownGlobals.push(global.performance); 307} 308if (global.PerformanceMark) { 309 knownGlobals.push(global.PerformanceMark); 310} 311if (global.PerformanceMeasure) { 312 knownGlobals.push(global.PerformanceMeasure); 313} 314 315// TODO(@ethan-arrowood): Similar to previous checks, this can be temporary 316// until v16.x is EOL. Once all supported versions have structuredClone we 317// can add this to the list above instead. 318if (global.structuredClone) { 319 knownGlobals.push(global.structuredClone); 320} 321 322if (global.fetch) { 323 knownGlobals.push(fetch); 324} 325if (hasCrypto && global.crypto) { 326 knownGlobals.push(global.crypto); 327 knownGlobals.push(global.Crypto); 328 knownGlobals.push(global.CryptoKey); 329 knownGlobals.push(global.SubtleCrypto); 330} 331if (global.CustomEvent) { 332 knownGlobals.push(global.CustomEvent); 333} 334if (global.ReadableStream) { 335 knownGlobals.push( 336 global.ReadableStream, 337 global.ReadableStreamDefaultReader, 338 global.ReadableStreamBYOBReader, 339 global.ReadableStreamBYOBRequest, 340 global.ReadableByteStreamController, 341 global.ReadableStreamDefaultController, 342 global.TransformStream, 343 global.TransformStreamDefaultController, 344 global.WritableStream, 345 global.WritableStreamDefaultWriter, 346 global.WritableStreamDefaultController, 347 global.ByteLengthQueuingStrategy, 348 global.CountQueuingStrategy, 349 global.TextEncoderStream, 350 global.TextDecoderStream, 351 global.CompressionStream, 352 global.DecompressionStream, 353 ); 354} 355 356function allowGlobals(...allowlist) { 357 knownGlobals = knownGlobals.concat(allowlist); 358} 359 360if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { 361 if (process.env.NODE_TEST_KNOWN_GLOBALS) { 362 const knownFromEnv = process.env.NODE_TEST_KNOWN_GLOBALS.split(','); 363 allowGlobals(...knownFromEnv); 364 } 365 366 function leakedGlobals() { 367 const leaked = []; 368 369 for (const val in global) { 370 if (!knownGlobals.includes(global[val])) { 371 leaked.push(val); 372 } 373 } 374 375 return leaked; 376 } 377 378 process.on('exit', function() { 379 const leaked = leakedGlobals(); 380 if (leaked.length > 0) { 381 assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}`); 382 } 383 }); 384} 385 386const mustCallChecks = []; 387 388function runCallChecks(exitCode) { 389 if (exitCode !== 0) return; 390 391 const failed = mustCallChecks.filter(function(context) { 392 if ('minimum' in context) { 393 context.messageSegment = `at least ${context.minimum}`; 394 return context.actual < context.minimum; 395 } 396 context.messageSegment = `exactly ${context.exact}`; 397 return context.actual !== context.exact; 398 }); 399 400 failed.forEach(function(context) { 401 console.log('Mismatched %s function calls. Expected %s, actual %d.', 402 context.name, 403 context.messageSegment, 404 context.actual); 405 console.log(context.stack.split('\n').slice(2).join('\n')); 406 }); 407 408 if (failed.length) process.exit(1); 409} 410 411function mustCall(fn, exact) { 412 return _mustCallInner(fn, exact, 'exact'); 413} 414 415function mustSucceed(fn, exact) { 416 return mustCall(function(err, ...args) { 417 assert.ifError(err); 418 if (typeof fn === 'function') 419 return fn.apply(this, args); 420 }, exact); 421} 422 423function mustCallAtLeast(fn, minimum) { 424 return _mustCallInner(fn, minimum, 'minimum'); 425} 426 427function _mustCallInner(fn, criteria = 1, field) { 428 if (process._exiting) 429 throw new Error('Cannot use common.mustCall*() in process exit handler'); 430 if (typeof fn === 'number') { 431 criteria = fn; 432 fn = noop; 433 } else if (fn === undefined) { 434 fn = noop; 435 } 436 437 if (typeof criteria !== 'number') 438 throw new TypeError(`Invalid ${field} value: ${criteria}`); 439 440 const context = { 441 [field]: criteria, 442 actual: 0, 443 stack: inspect(new Error()), 444 name: fn.name || '<anonymous>', 445 }; 446 447 // Add the exit listener only once to avoid listener leak warnings 448 if (mustCallChecks.length === 0) process.on('exit', runCallChecks); 449 450 mustCallChecks.push(context); 451 452 const _return = function() { // eslint-disable-line func-style 453 context.actual++; 454 return fn.apply(this, arguments); 455 }; 456 // Function instances have own properties that may be relevant. 457 // Let's replicate those properties to the returned function. 458 // Refs: https://tc39.es/ecma262/#sec-function-instances 459 Object.defineProperties(_return, { 460 name: { 461 value: fn.name, 462 writable: false, 463 enumerable: false, 464 configurable: true, 465 }, 466 length: { 467 value: fn.length, 468 writable: false, 469 enumerable: false, 470 configurable: true, 471 }, 472 }); 473 return _return; 474} 475 476function hasMultiLocalhost() { 477 const { internalBinding } = require('internal/test/binding'); 478 const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap'); 479 const t = new TCP(TCPConstants.SOCKET); 480 const ret = t.bind('127.0.0.2', 0); 481 t.close(); 482 return ret === 0; 483} 484 485function skipIfEslintMissing() { 486 if (!fs.existsSync( 487 path.join(__dirname, '..', '..', 'tools', 'node_modules', 'eslint'), 488 )) { 489 skip('missing ESLint'); 490 } 491} 492 493function canCreateSymLink() { 494 // On Windows, creating symlinks requires admin privileges. 495 // We'll only try to run symlink test if we have enough privileges. 496 // On other platforms, creating symlinks shouldn't need admin privileges 497 if (isWindows) { 498 // whoami.exe needs to be the one from System32 499 // If unix tools are in the path, they can shadow the one we want, 500 // so use the full path while executing whoami 501 const whoamiPath = path.join(process.env.SystemRoot, 502 'System32', 'whoami.exe'); 503 504 try { 505 const output = execSync(`${whoamiPath} /priv`, { timeout: 1000 }); 506 return output.includes('SeCreateSymbolicLinkPrivilege'); 507 } catch { 508 return false; 509 } 510 } 511 // On non-Windows platforms, this always returns `true` 512 return true; 513} 514 515function getCallSite(top) { 516 const originalStackFormatter = Error.prepareStackTrace; 517 Error.prepareStackTrace = (err, stack) => 518 `${stack[0].getFileName()}:${stack[0].getLineNumber()}`; 519 const err = new Error(); 520 Error.captureStackTrace(err, top); 521 // With the V8 Error API, the stack is not formatted until it is accessed 522 err.stack; // eslint-disable-line no-unused-expressions 523 Error.prepareStackTrace = originalStackFormatter; 524 return err.stack; 525} 526 527function mustNotCall(msg) { 528 const callSite = getCallSite(mustNotCall); 529 return function mustNotCall(...args) { 530 const argsInfo = args.length > 0 ? 531 `\ncalled with arguments: ${args.map((arg) => inspect(arg)).join(', ')}` : ''; 532 assert.fail( 533 `${msg || 'function should not have been called'} at ${callSite}` + 534 argsInfo); 535 }; 536} 537 538const _mustNotMutateObjectDeepProxies = new WeakMap(); 539 540function mustNotMutateObjectDeep(original) { 541 // Return primitives and functions directly. Primitives are immutable, and 542 // proxied functions are impossible to compare against originals, e.g. with 543 // `assert.deepEqual()`. 544 if (original === null || typeof original !== 'object') { 545 return original; 546 } 547 548 const cachedProxy = _mustNotMutateObjectDeepProxies.get(original); 549 if (cachedProxy) { 550 return cachedProxy; 551 } 552 553 const _mustNotMutateObjectDeepHandler = { 554 __proto__: null, 555 defineProperty(target, property, descriptor) { 556 assert.fail(`Expected no side effects, got ${inspect(property)} ` + 557 'defined'); 558 }, 559 deleteProperty(target, property) { 560 assert.fail(`Expected no side effects, got ${inspect(property)} ` + 561 'deleted'); 562 }, 563 get(target, prop, receiver) { 564 return mustNotMutateObjectDeep(Reflect.get(target, prop, receiver)); 565 }, 566 preventExtensions(target) { 567 assert.fail('Expected no side effects, got extensions prevented on ' + 568 inspect(target)); 569 }, 570 set(target, property, value, receiver) { 571 assert.fail(`Expected no side effects, got ${inspect(value)} ` + 572 `assigned to ${inspect(property)}`); 573 }, 574 setPrototypeOf(target, prototype) { 575 assert.fail(`Expected no side effects, got set prototype to ${prototype}`); 576 }, 577 }; 578 579 const proxy = new Proxy(original, _mustNotMutateObjectDeepHandler); 580 _mustNotMutateObjectDeepProxies.set(original, proxy); 581 return proxy; 582} 583 584function printSkipMessage(msg) { 585 console.log(`1..0 # Skipped: ${msg}`); 586} 587 588function skip(msg) { 589 printSkipMessage(msg); 590 process.exit(0); 591} 592 593// Returns true if the exit code "exitCode" and/or signal name "signal" 594// represent the exit code and/or signal name of a node process that aborted, 595// false otherwise. 596function nodeProcessAborted(exitCode, signal) { 597 // Depending on the compiler used, node will exit with either 598 // exit code 132 (SIGILL), 133 (SIGTRAP) or 134 (SIGABRT). 599 let expectedExitCodes = [132, 133, 134]; 600 601 // On platforms using KSH as the default shell (like SmartOS), 602 // when a process aborts, KSH exits with an exit code that is 603 // greater than 256, and thus the exit code emitted with the 'exit' 604 // event is null and the signal is set to either SIGILL, SIGTRAP, 605 // or SIGABRT (depending on the compiler). 606 const expectedSignals = ['SIGILL', 'SIGTRAP', 'SIGABRT']; 607 608 // On Windows, 'aborts' are of 2 types, depending on the context: 609 // (i) Exception breakpoint, if --abort-on-uncaught-exception is on 610 // which corresponds to exit code 2147483651 (0x80000003) 611 // (ii) Otherwise, _exit(134) which is called in place of abort() due to 612 // raising SIGABRT exiting with ambiguous exit code '3' by default 613 if (isWindows) 614 expectedExitCodes = [0x80000003, 134]; 615 616 // When using --abort-on-uncaught-exception, V8 will use 617 // base::OS::Abort to terminate the process. 618 // Depending on the compiler used, the shell or other aspects of 619 // the platform used to build the node binary, this will actually 620 // make V8 exit by aborting or by raising a signal. In any case, 621 // one of them (exit code or signal) needs to be set to one of 622 // the expected exit codes or signals. 623 if (signal !== null) { 624 return expectedSignals.includes(signal); 625 } 626 return expectedExitCodes.includes(exitCode); 627} 628 629function isAlive(pid) { 630 try { 631 process.kill(pid, 'SIGCONT'); 632 return true; 633 } catch { 634 return false; 635 } 636} 637 638function _expectWarning(name, expected, code) { 639 if (typeof expected === 'string') { 640 expected = [[expected, code]]; 641 } else if (!Array.isArray(expected)) { 642 expected = Object.entries(expected).map(([a, b]) => [b, a]); 643 } else if (!(Array.isArray(expected[0]))) { 644 expected = [[expected[0], expected[1]]]; 645 } 646 // Deprecation codes are mandatory, everything else is not. 647 if (name === 'DeprecationWarning') { 648 expected.forEach(([_, code]) => assert(code, expected)); 649 } 650 return mustCall((warning) => { 651 const expectedProperties = expected.shift(); 652 if (!expectedProperties) { 653 assert.fail(`Unexpected extra warning received: ${warning}`); 654 } 655 const [ message, code ] = expectedProperties; 656 assert.strictEqual(warning.name, name); 657 if (typeof message === 'string') { 658 assert.strictEqual(warning.message, message); 659 } else { 660 assert.match(warning.message, message); 661 } 662 assert.strictEqual(warning.code, code); 663 }, expected.length); 664} 665 666let catchWarning; 667 668// Accepts a warning name and description or array of descriptions or a map of 669// warning names to description(s) ensures a warning is generated for each 670// name/description pair. 671// The expected messages have to be unique per `expectWarning()` call. 672function expectWarning(nameOrMap, expected, code) { 673 if (catchWarning === undefined) { 674 catchWarning = {}; 675 process.on('warning', (warning) => { 676 if (!catchWarning[warning.name]) { 677 throw new TypeError( 678 `"${warning.name}" was triggered without being expected.\n` + 679 inspect(warning), 680 ); 681 } 682 catchWarning[warning.name](warning); 683 }); 684 } 685 if (typeof nameOrMap === 'string') { 686 catchWarning[nameOrMap] = _expectWarning(nameOrMap, expected, code); 687 } else { 688 Object.keys(nameOrMap).forEach((name) => { 689 catchWarning[name] = _expectWarning(name, nameOrMap[name]); 690 }); 691 } 692} 693 694// Useful for testing expected internal/error objects 695function expectsError(validator, exact) { 696 return mustCall((...args) => { 697 if (args.length !== 1) { 698 // Do not use `assert.strictEqual()` to prevent `inspect` from 699 // always being called. 700 assert.fail(`Expected one argument, got ${inspect(args)}`); 701 } 702 const error = args.pop(); 703 const descriptor = Object.getOwnPropertyDescriptor(error, 'message'); 704 // The error message should be non-enumerable 705 assert.strictEqual(descriptor.enumerable, false); 706 707 assert.throws(() => { throw error; }, validator); 708 return true; 709 }, exact); 710} 711 712function skipIfInspectorDisabled() { 713 if (!process.features.inspector) { 714 skip('V8 inspector is disabled'); 715 } 716} 717 718function skipIf32Bits() { 719 if (bits < 64) { 720 skip('The tested feature is not available in 32bit builds'); 721 } 722} 723 724function skipIfWorker() { 725 if (!isMainThread) { 726 skip('This test only works on a main thread'); 727 } 728} 729 730function getArrayBufferViews(buf) { 731 const { buffer, byteOffset, byteLength } = buf; 732 733 const out = []; 734 735 const arrayBufferViews = [ 736 Int8Array, 737 Uint8Array, 738 Uint8ClampedArray, 739 Int16Array, 740 Uint16Array, 741 Int32Array, 742 Uint32Array, 743 Float32Array, 744 Float64Array, 745 BigInt64Array, 746 BigUint64Array, 747 DataView, 748 ]; 749 750 for (const type of arrayBufferViews) { 751 const { BYTES_PER_ELEMENT = 1 } = type; 752 if (byteLength % BYTES_PER_ELEMENT === 0) { 753 out.push(new type(buffer, byteOffset, byteLength / BYTES_PER_ELEMENT)); 754 } 755 } 756 return out; 757} 758 759function getBufferSources(buf) { 760 return [...getArrayBufferViews(buf), new Uint8Array(buf).buffer]; 761} 762 763function getTTYfd() { 764 // Do our best to grab a tty fd. 765 const tty = require('tty'); 766 // Don't attempt fd 0 as it is not writable on Windows. 767 // Ref: ef2861961c3d9e9ed6972e1e84d969683b25cf95 768 const ttyFd = [1, 2, 4, 5].find(tty.isatty); 769 if (ttyFd === undefined) { 770 try { 771 return fs.openSync('/dev/tty'); 772 } catch { 773 // There aren't any tty fd's available to use. 774 return -1; 775 } 776 } 777 return ttyFd; 778} 779 780function runWithInvalidFD(func) { 781 let fd = 1 << 30; 782 // Get first known bad file descriptor. 1 << 30 is usually unlikely to 783 // be an valid one. 784 try { 785 while (fs.fstatSync(fd--) && fd > 0); 786 } catch { 787 return func(fd); 788 } 789 790 printSkipMessage('Could not generate an invalid fd'); 791} 792 793// A helper function to simplify checking for ERR_INVALID_ARG_TYPE output. 794function invalidArgTypeHelper(input) { 795 if (input == null) { 796 return ` Received ${input}`; 797 } 798 if (typeof input === 'function' && input.name) { 799 return ` Received function ${input.name}`; 800 } 801 if (typeof input === 'object') { 802 if (input.constructor?.name) { 803 return ` Received an instance of ${input.constructor.name}`; 804 } 805 return ` Received ${inspect(input, { depth: -1 })}`; 806 } 807 808 let inspected = inspect(input, { colors: false }); 809 if (inspected.length > 28) { inspected = `${inspected.slice(inspected, 0, 25)}...`; } 810 811 return ` Received type ${typeof input} (${inspected})`; 812} 813 814function skipIfDumbTerminal() { 815 if (isDumbTerminal) { 816 skip('skipping - dumb terminal'); 817 } 818} 819 820function gcUntil(name, condition) { 821 if (typeof name === 'function') { 822 condition = name; 823 name = undefined; 824 } 825 return new Promise((resolve, reject) => { 826 let count = 0; 827 function gcAndCheck() { 828 setImmediate(() => { 829 count++; 830 global.gc(); 831 if (condition()) { 832 resolve(); 833 } else if (count < 10) { 834 gcAndCheck(); 835 } else { 836 reject(name === undefined ? undefined : 'Test ' + name + ' failed'); 837 } 838 }); 839 } 840 gcAndCheck(); 841 }); 842} 843 844function requireNoPackageJSONAbove(dir = __dirname) { 845 let possiblePackage = path.join(dir, '..', 'package.json'); 846 let lastPackage = null; 847 while (possiblePackage !== lastPackage) { 848 if (fs.existsSync(possiblePackage)) { 849 assert.fail( 850 'This test shouldn\'t load properties from a package.json above ' + 851 `its file location. Found package.json at ${possiblePackage}.`); 852 } 853 lastPackage = possiblePackage; 854 possiblePackage = path.join(possiblePackage, '..', '..', 'package.json'); 855 } 856} 857 858function spawnPromisified(...args) { 859 let stderr = ''; 860 let stdout = ''; 861 862 const child = spawn(...args); 863 child.stderr.setEncoding('utf8'); 864 child.stderr.on('data', (data) => { stderr += data; }); 865 child.stdout.setEncoding('utf8'); 866 child.stdout.on('data', (data) => { stdout += data; }); 867 868 return new Promise((resolve, reject) => { 869 child.on('close', (code, signal) => { 870 resolve({ 871 code, 872 signal, 873 stderr, 874 stdout, 875 }); 876 }); 877 child.on('error', (code, signal) => { 878 reject({ 879 code, 880 signal, 881 stderr, 882 stdout, 883 }); 884 }); 885 }); 886} 887 888const common = { 889 allowGlobals, 890 buildType, 891 canCreateSymLink, 892 childShouldThrowAndAbort, 893 createZeroFilledFile, 894 expectsError, 895 expectWarning, 896 gcUntil, 897 getArrayBufferViews, 898 getBufferSources, 899 getCallSite, 900 getTTYfd, 901 hasIntl, 902 hasCrypto, 903 hasOpenSSL3, 904 hasOpenSSL31, 905 hasQuic, 906 hasMultiLocalhost, 907 invalidArgTypeHelper, 908 isAlive, 909 isAsan, 910 isDumbTerminal, 911 isFreeBSD, 912 isLinux, 913 isMainThread, 914 isOpenBSD, 915 isOSX, 916 isPi, 917 isSunOS, 918 isWindows, 919 localIPv6Hosts, 920 mustCall, 921 mustCallAtLeast, 922 mustNotCall, 923 mustNotMutateObjectDeep, 924 mustSucceed, 925 nodeProcessAborted, 926 PIPE, 927 parseTestFlags, 928 platformTimeout, 929 printSkipMessage, 930 pwdCommand, 931 requireNoPackageJSONAbove, 932 runWithInvalidFD, 933 skip, 934 skipIf32Bits, 935 skipIfDumbTerminal, 936 skipIfEslintMissing, 937 skipIfInspectorDisabled, 938 skipIfWorker, 939 spawnPromisified, 940 941 get enoughTestMem() { 942 return require('os').totalmem() > 0x70000000; /* 1.75 Gb */ 943 }, 944 945 get hasFipsCrypto() { 946 return hasCrypto && require('crypto').getFips(); 947 }, 948 949 get hasIPv6() { 950 const iFaces = require('os').networkInterfaces(); 951 let re; 952 if (isWindows) { 953 re = /Loopback Pseudo-Interface/; 954 } else if (this.isIBMi) { 955 re = /\*LOOPBACK/; 956 } else { 957 re = /lo/; 958 } 959 return Object.keys(iFaces).some((name) => { 960 return re.test(name) && 961 iFaces[name].some(({ family }) => family === 'IPv6'); 962 }); 963 }, 964 965 get inFreeBSDJail() { 966 if (inFreeBSDJail !== null) return inFreeBSDJail; 967 968 if (exports.isFreeBSD && 969 execSync('sysctl -n security.jail.jailed').toString() === '1\n') { 970 inFreeBSDJail = true; 971 } else { 972 inFreeBSDJail = false; 973 } 974 return inFreeBSDJail; 975 }, 976 977 // On IBMi, process.platform and os.platform() both return 'aix', 978 // when built with Python versions earlier than 3.9. 979 // It is not enough to differentiate between IBMi and real AIX system. 980 get isAIX() { 981 return require('os').type() === 'AIX'; 982 }, 983 984 get isIBMi() { 985 return require('os').type() === 'OS400'; 986 }, 987 988 get isLinuxPPCBE() { 989 return (process.platform === 'linux') && (process.arch === 'ppc64') && 990 (require('os').endianness() === 'BE'); 991 }, 992 993 get localhostIPv4() { 994 if (localhostIPv4 !== null) return localhostIPv4; 995 996 if (this.inFreeBSDJail) { 997 // Jailed network interfaces are a bit special - since we need to jump 998 // through loops, as well as this being an exception case, assume the 999 // user will provide this instead. 1000 if (process.env.LOCALHOST) { 1001 localhostIPv4 = process.env.LOCALHOST; 1002 } else { 1003 console.error('Looks like we\'re in a FreeBSD Jail. ' + 1004 'Please provide your default interface address ' + 1005 'as LOCALHOST or expect some tests to fail.'); 1006 } 1007 } 1008 1009 if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1'; 1010 1011 return localhostIPv4; 1012 }, 1013 1014 // opensslCli defined lazily to reduce overhead of spawnSync 1015 get opensslCli() { 1016 if (opensslCli !== null) return opensslCli; 1017 1018 if (process.config.variables.node_shared_openssl) { 1019 // Use external command 1020 opensslCli = 'openssl'; 1021 } else { 1022 // Use command built from sources included in Node.js repository 1023 opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli'); 1024 } 1025 1026 if (exports.isWindows) opensslCli += '.exe'; 1027 1028 const opensslCmd = spawnSync(opensslCli, ['version']); 1029 if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) { 1030 // OpenSSL command cannot be executed 1031 opensslCli = false; 1032 } 1033 return opensslCli; 1034 }, 1035 1036 get PORT() { 1037 if (+process.env.TEST_PARALLEL) { 1038 throw new Error('common.PORT cannot be used in a parallelized test'); 1039 } 1040 return +process.env.NODE_COMMON_PORT || 12346; 1041 }, 1042 1043 /** 1044 * Returns the EOL character used by this Git checkout. 1045 */ 1046 get checkoutEOL() { 1047 return fs.readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; 1048 }, 1049}; 1050 1051const validProperties = new Set(Object.keys(common)); 1052module.exports = new Proxy(common, { 1053 get(obj, prop) { 1054 if (!validProperties.has(prop)) 1055 throw new Error(`Using invalid common property: '${prop}'`); 1056 return obj[prop]; 1057 }, 1058}); 1059