• 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 >= 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