• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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