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