• 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/require-common-first, node-core/required-modules */
23/* eslint-disable node-core/crypto-check */
24'use strict';
25const process = global.process;  // Some tests tamper with the process global.
26
27const assert = require('assert');
28const { exec, execSync, spawnSync } = require('child_process');
29const fs = require('fs');
30// Do not require 'os' until needed so that test-os-checked-function can
31// monkey patch it. If 'os' is required here, that test will fail.
32const path = require('path');
33const util = require('util');
34const { isMainThread } = require('worker_threads');
35
36const tmpdir = require('./tmpdir');
37const bits = ['arm64', 'mips', 'mipsel', 'ppc64', 's390x', 'x64']
38  .includes(process.arch) ? 64 : 32;
39const hasIntl = !!process.config.variables.v8_enable_i18n_support;
40
41// Some tests assume a umask of 0o022 so set that up front. Tests that need a
42// different umask will set it themselves.
43//
44// Workers can read, but not set the umask, so check that this is the main
45// thread.
46if (isMainThread)
47  process.umask(0o022);
48
49const noop = () => {};
50
51const hasCrypto = Boolean(process.versions.openssl) &&
52                  !process.env.NODE_SKIP_CRYPTO;
53
54// Check for flags. Skip this for workers (both, the `cluster` module and
55// `worker_threads`) and child processes.
56// If the binary was built without-ssl then the crypto flags are
57// invalid (bad option). The test itself should handle this case.
58if (process.argv.length === 2 &&
59    !process.env.NODE_SKIP_FLAG_CHECK &&
60    isMainThread &&
61    hasCrypto &&
62    module.parent &&
63    require('cluster').isMaster) {
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(module.parent.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.indexOf('// Flags: --') + 10;
73  if (flagStart !== 9) {
74    let flagEnd = source.indexOf('\n', flagStart);
75    // Normalize different EOL.
76    if (source[flagEnd - 1] === '\r') {
77      flagEnd--;
78    }
79    const flags = source
80      .substring(flagStart, flagEnd)
81      .replace(/_/g, '-')
82      .split(' ');
83    const args = process.execArgv.map((arg) => arg.replace(/_/g, '-'));
84    for (const flag of flags) {
85      if (!args.includes(flag) &&
86          // If the binary is build without `intl` the inspect option is
87          // invalid. The test itself should handle this case.
88          (process.features.inspector || !flag.startsWith('--inspect'))) {
89        console.log(
90          'NOTE: The test started as a child_process using these flags:',
91          util.inspect(flags),
92          'Use NODE_SKIP_FLAG_CHECK to run the test with the original flags.'
93        );
94        const args = [...flags, ...process.execArgv, ...process.argv.slice(1)];
95        const options = { encoding: 'utf8', stdio: 'inherit' };
96        const result = spawnSync(process.execPath, args, options);
97        if (result.signal) {
98          process.kill(0, result.signal);
99        } else {
100          process.exit(result.status);
101        }
102      }
103    }
104  }
105}
106
107const isWindows = process.platform === 'win32';
108const isAIX = process.platform === 'aix';
109const isSunOS = process.platform === 'sunos';
110const isFreeBSD = process.platform === 'freebsd';
111const isOpenBSD = process.platform === 'openbsd';
112const isLinux = process.platform === 'linux';
113const isOSX = process.platform === 'darwin';
114
115const isDumbTerminal = process.env.TERM === 'dumb';
116
117const buildType = process.config.target_defaults ?
118  process.config.target_defaults.default_configuration :
119  'Release';
120
121// If env var is set then enable async_hook hooks for all tests.
122if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) {
123  const destroydIdsList = {};
124  const destroyListList = {};
125  const initHandles = {};
126  const { internalBinding } = require('internal/test/binding');
127  const async_wrap = internalBinding('async_wrap');
128
129  process.on('exit', () => {
130    // Iterate through handles to make sure nothing crashes
131    for (const k in initHandles)
132      util.inspect(initHandles[k]);
133  });
134
135  const _queueDestroyAsyncId = async_wrap.queueDestroyAsyncId;
136  async_wrap.queueDestroyAsyncId = function queueDestroyAsyncId(id) {
137    if (destroyListList[id] !== undefined) {
138      process._rawDebug(destroyListList[id]);
139      process._rawDebug();
140      throw new Error(`same id added to destroy list twice (${id})`);
141    }
142    destroyListList[id] = util.inspect(new Error());
143    _queueDestroyAsyncId(id);
144  };
145
146  require('async_hooks').createHook({
147    init(id, ty, tr, resource) {
148      if (initHandles[id]) {
149        process._rawDebug(
150          `Is same resource: ${resource === initHandles[id].resource}`);
151        process._rawDebug(`Previous stack:\n${initHandles[id].stack}\n`);
152        throw new Error(`init called twice for same id (${id})`);
153      }
154      initHandles[id] = {
155        resource,
156        stack: util.inspect(new Error()).substr(6)
157      };
158    },
159    before() { },
160    after() { },
161    destroy(id) {
162      if (destroydIdsList[id] !== undefined) {
163        process._rawDebug(destroydIdsList[id]);
164        process._rawDebug();
165        throw new Error(`destroy called for same id (${id})`);
166      }
167      destroydIdsList[id] = util.inspect(new Error());
168    },
169  }).enable();
170}
171
172let opensslCli = null;
173let inFreeBSDJail = null;
174let localhostIPv4 = null;
175
176const localIPv6Hosts =
177  isLinux ? [
178    // Debian/Ubuntu
179    'ip6-localhost',
180    'ip6-loopback',
181
182    // SUSE
183    'ipv6-localhost',
184    'ipv6-loopback',
185
186    // Typically universal
187    'localhost',
188  ] : [ 'localhost' ];
189
190const PIPE = (() => {
191  const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`);
192  const pipePrefix = isWindows ? '\\\\.\\pipe\\' : localRelative;
193  const pipeName = `node-test.${process.pid}.sock`;
194  return path.join(pipePrefix, pipeName);
195})();
196
197/*
198 * Check that when running a test with
199 * `$node --abort-on-uncaught-exception $file child`
200 * the process aborts.
201 */
202function childShouldThrowAndAbort() {
203  let testCmd = '';
204  if (!isWindows) {
205    // Do not create core files, as it can take a lot of disk space on
206    // continuous testing and developers' machines
207    testCmd += 'ulimit -c 0 && ';
208  }
209  testCmd += `"${process.argv[0]}" --abort-on-uncaught-exception `;
210  testCmd += `"${process.argv[1]}" child`;
211  const child = exec(testCmd);
212  child.on('exit', function onExit(exitCode, signal) {
213    const errMsg = 'Test should have aborted ' +
214                   `but instead exited with exit code ${exitCode}` +
215                   ` and signal ${signal}`;
216    assert(nodeProcessAborted(exitCode, signal), errMsg);
217  });
218}
219
220function createZeroFilledFile(filename) {
221  const fd = fs.openSync(filename, 'w');
222  fs.ftruncateSync(fd, 10 * 1024 * 1024);
223  fs.closeSync(fd);
224}
225
226
227const pwdCommand = isWindows ?
228  ['cmd.exe', ['/d', '/c', 'cd']] :
229  ['pwd', []];
230
231
232function platformTimeout(ms) {
233  const multipliers = typeof ms === 'bigint' ?
234    { two: 2n, four: 4n, seven: 7n } : { two: 2, four: 4, seven: 7 };
235
236  if (process.features.debug)
237    ms = multipliers.two * ms;
238
239  if (isAIX)
240    return multipliers.two * ms; // Default localhost speed is slower on AIX
241
242  if (process.arch !== 'arm')
243    return ms;
244
245  const armv = process.config.variables.arm_version;
246
247  if (armv === '6')
248    return multipliers.seven * ms;  // ARMv6
249
250  if (armv === '7')
251    return multipliers.two * ms;  // ARMv7
252
253  return ms; // ARMv8+
254}
255
256let knownGlobals = [
257  clearImmediate,
258  clearInterval,
259  clearTimeout,
260  global,
261  setImmediate,
262  setInterval,
263  setTimeout,
264  queueMicrotask,
265];
266
267if (global.gc) {
268  knownGlobals.push(global.gc);
269}
270
271function allowGlobals(...whitelist) {
272  knownGlobals = knownGlobals.concat(whitelist);
273}
274
275if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
276  if (process.env.NODE_TEST_KNOWN_GLOBALS) {
277    const knownFromEnv = process.env.NODE_TEST_KNOWN_GLOBALS.split(',');
278    allowGlobals(...knownFromEnv);
279  }
280
281  function leakedGlobals() {
282    const leaked = [];
283
284    for (const val in global) {
285      if (!knownGlobals.includes(global[val])) {
286        leaked.push(val);
287      }
288    }
289
290    return leaked;
291  }
292
293  process.on('exit', function() {
294    const leaked = leakedGlobals();
295    if (leaked.length > 0) {
296      assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}`);
297    }
298  });
299}
300
301const mustCallChecks = [];
302
303function runCallChecks(exitCode) {
304  if (exitCode !== 0) return;
305
306  const failed = mustCallChecks.filter(function(context) {
307    if ('minimum' in context) {
308      context.messageSegment = `at least ${context.minimum}`;
309      return context.actual < context.minimum;
310    }
311    context.messageSegment = `exactly ${context.exact}`;
312    return context.actual !== context.exact;
313  });
314
315  failed.forEach(function(context) {
316    console.log('Mismatched %s function calls. Expected %s, actual %d.',
317                context.name,
318                context.messageSegment,
319                context.actual);
320    console.log(context.stack.split('\n').slice(2).join('\n'));
321  });
322
323  if (failed.length) process.exit(1);
324}
325
326function mustCall(fn, exact) {
327  return _mustCallInner(fn, exact, 'exact');
328}
329
330function mustCallAtLeast(fn, minimum) {
331  return _mustCallInner(fn, minimum, 'minimum');
332}
333
334function _mustCallInner(fn, criteria = 1, field) {
335  if (process._exiting)
336    throw new Error('Cannot use common.mustCall*() in process exit handler');
337  if (typeof fn === 'number') {
338    criteria = fn;
339    fn = noop;
340  } else if (fn === undefined) {
341    fn = noop;
342  }
343
344  if (typeof criteria !== 'number')
345    throw new TypeError(`Invalid ${field} value: ${criteria}`);
346
347  const context = {
348    [field]: criteria,
349    actual: 0,
350    stack: util.inspect(new Error()),
351    name: fn.name || '<anonymous>'
352  };
353
354  // Add the exit listener only once to avoid listener leak warnings
355  if (mustCallChecks.length === 0) process.on('exit', runCallChecks);
356
357  mustCallChecks.push(context);
358
359  return function() {
360    context.actual++;
361    return fn.apply(this, arguments);
362  };
363}
364
365function hasMultiLocalhost() {
366  const { internalBinding } = require('internal/test/binding');
367  const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap');
368  const t = new TCP(TCPConstants.SOCKET);
369  const ret = t.bind('127.0.0.2', 0);
370  t.close();
371  return ret === 0;
372}
373
374function skipIfEslintMissing() {
375  if (!fs.existsSync(
376    path.join(__dirname, '..', '..', 'tools', 'node_modules', 'eslint')
377  )) {
378    skip('missing ESLint');
379  }
380}
381
382function canCreateSymLink() {
383  // On Windows, creating symlinks requires admin privileges.
384  // We'll only try to run symlink test if we have enough privileges.
385  // On other platforms, creating symlinks shouldn't need admin privileges
386  if (isWindows) {
387    // whoami.exe needs to be the one from System32
388    // If unix tools are in the path, they can shadow the one we want,
389    // so use the full path while executing whoami
390    const whoamiPath = path.join(process.env.SystemRoot,
391                                 'System32', 'whoami.exe');
392
393    try {
394      const output = execSync(`${whoamiPath} /priv`, { timeout: 1000 });
395      return output.includes('SeCreateSymbolicLinkPrivilege');
396    } catch {
397      return false;
398    }
399  }
400  // On non-Windows platforms, this always returns `true`
401  return true;
402}
403
404function getCallSite(top) {
405  const originalStackFormatter = Error.prepareStackTrace;
406  Error.prepareStackTrace = (err, stack) =>
407    `${stack[0].getFileName()}:${stack[0].getLineNumber()}`;
408  const err = new Error();
409  Error.captureStackTrace(err, top);
410  // With the V8 Error API, the stack is not formatted until it is accessed
411  err.stack;
412  Error.prepareStackTrace = originalStackFormatter;
413  return err.stack;
414}
415
416function mustNotCall(msg) {
417  const callSite = getCallSite(mustNotCall);
418  return function mustNotCall(...args) {
419    const argsInfo = args.length > 0 ?
420      `\ncalled with arguments: ${args.map(util.inspect).join(', ')}` : '';
421    assert.fail(
422      `${msg || 'function should not have been called'} at ${callSite}` +
423      argsInfo);
424  };
425}
426
427function printSkipMessage(msg) {
428  console.log(`1..0 # Skipped: ${msg}`);
429}
430
431function skip(msg) {
432  printSkipMessage(msg);
433  process.exit(0);
434}
435
436// Returns true if the exit code "exitCode" and/or signal name "signal"
437// represent the exit code and/or signal name of a node process that aborted,
438// false otherwise.
439function nodeProcessAborted(exitCode, signal) {
440  // Depending on the compiler used, node will exit with either
441  // exit code 132 (SIGILL), 133 (SIGTRAP) or 134 (SIGABRT).
442  let expectedExitCodes = [132, 133, 134];
443
444  // On platforms using KSH as the default shell (like SmartOS),
445  // when a process aborts, KSH exits with an exit code that is
446  // greater than 256, and thus the exit code emitted with the 'exit'
447  // event is null and the signal is set to either SIGILL, SIGTRAP,
448  // or SIGABRT (depending on the compiler).
449  const expectedSignals = ['SIGILL', 'SIGTRAP', 'SIGABRT'];
450
451  // On Windows, 'aborts' are of 2 types, depending on the context:
452  // (i) Forced access violation, if --abort-on-uncaught-exception is on
453  // which corresponds to exit code 3221225477 (0xC0000005)
454  // (ii) Otherwise, _exit(134) which is called in place of abort() due to
455  // raising SIGABRT exiting with ambiguous exit code '3' by default
456  if (isWindows)
457    expectedExitCodes = [0xC0000005, 134];
458
459  // When using --abort-on-uncaught-exception, V8 will use
460  // base::OS::Abort to terminate the process.
461  // Depending on the compiler used, the shell or other aspects of
462  // the platform used to build the node binary, this will actually
463  // make V8 exit by aborting or by raising a signal. In any case,
464  // one of them (exit code or signal) needs to be set to one of
465  // the expected exit codes or signals.
466  if (signal !== null) {
467    return expectedSignals.includes(signal);
468  }
469  return expectedExitCodes.includes(exitCode);
470}
471
472function isAlive(pid) {
473  try {
474    process.kill(pid, 'SIGCONT');
475    return true;
476  } catch {
477    return false;
478  }
479}
480
481function _expectWarning(name, expected, code) {
482  if (typeof expected === 'string') {
483    expected = [[expected, code]];
484  } else if (!Array.isArray(expected)) {
485    expected = Object.entries(expected).map(([a, b]) => [b, a]);
486  } else if (!(Array.isArray(expected[0]))) {
487    expected = [[expected[0], expected[1]]];
488  }
489  // Deprecation codes are mandatory, everything else is not.
490  if (name === 'DeprecationWarning') {
491    expected.forEach(([_, code]) => assert(code, expected));
492  }
493  return mustCall((warning) => {
494    const [ message, code ] = expected.shift();
495    assert.strictEqual(warning.name, name);
496    if (typeof message === 'string') {
497      assert.strictEqual(warning.message, message);
498    } else {
499      assert(message.test(warning.message));
500    }
501    assert.strictEqual(warning.code, code);
502  }, expected.length);
503}
504
505let catchWarning;
506
507// Accepts a warning name and description or array of descriptions or a map of
508// warning names to description(s) ensures a warning is generated for each
509// name/description pair.
510// The expected messages have to be unique per `expectWarning()` call.
511function expectWarning(nameOrMap, expected, code) {
512  if (catchWarning === undefined) {
513    catchWarning = {};
514    process.on('warning', (warning) => {
515      if (!catchWarning[warning.name]) {
516        throw new TypeError(
517          `"${warning.name}" was triggered without being expected.\n` +
518          util.inspect(warning)
519        );
520      }
521      catchWarning[warning.name](warning);
522    });
523  }
524  if (typeof nameOrMap === 'string') {
525    catchWarning[nameOrMap] = _expectWarning(nameOrMap, expected, code);
526  } else {
527    Object.keys(nameOrMap).forEach((name) => {
528      catchWarning[name] = _expectWarning(name, nameOrMap[name]);
529    });
530  }
531}
532
533// Useful for testing expected internal/error objects
534function expectsError(validator, exact) {
535  return mustCall((...args) => {
536    if (args.length !== 1) {
537      // Do not use `assert.strictEqual()` to prevent `inspect` from
538      // always being called.
539      assert.fail(`Expected one argument, got ${util.inspect(args)}`);
540    }
541    const error = args.pop();
542    const descriptor = Object.getOwnPropertyDescriptor(error, 'message');
543    // The error message should be non-enumerable
544    assert.strictEqual(descriptor.enumerable, false);
545
546    assert.throws(() => { throw error; }, validator);
547    return true;
548  }, exact);
549}
550
551const suffix = 'This is caused by either a bug in Node.js ' +
552  'or incorrect usage of Node.js internals.\n' +
553  'Please open an issue with this stack trace at ' +
554  'https://github.com/nodejs/node/issues\n';
555
556function expectsInternalAssertion(fn, message) {
557  assert.throws(fn, {
558    message: `${message}\n${suffix}`,
559    name: 'Error',
560    code: 'ERR_INTERNAL_ASSERTION'
561  });
562}
563
564function skipIfInspectorDisabled() {
565  if (!process.features.inspector) {
566    skip('V8 inspector is disabled');
567  }
568}
569
570function skipIf32Bits() {
571  if (bits < 64) {
572    skip('The tested feature is not available in 32bit builds');
573  }
574}
575
576function skipIfWorker() {
577  if (!isMainThread) {
578    skip('This test only works on a main thread');
579  }
580}
581
582function getArrayBufferViews(buf) {
583  const { buffer, byteOffset, byteLength } = buf;
584
585  const out = [];
586
587  const arrayBufferViews = [
588    Int8Array,
589    Uint8Array,
590    Uint8ClampedArray,
591    Int16Array,
592    Uint16Array,
593    Int32Array,
594    Uint32Array,
595    Float32Array,
596    Float64Array,
597    DataView
598  ];
599
600  for (const type of arrayBufferViews) {
601    const { BYTES_PER_ELEMENT = 1 } = type;
602    if (byteLength % BYTES_PER_ELEMENT === 0) {
603      out.push(new type(buffer, byteOffset, byteLength / BYTES_PER_ELEMENT));
604    }
605  }
606  return out;
607}
608
609function getBufferSources(buf) {
610  return [...getArrayBufferViews(buf), new Uint8Array(buf).buffer];
611}
612
613// Crash the process on unhandled rejections.
614const crashOnUnhandledRejection = (err) => { throw err; };
615process.on('unhandledRejection', crashOnUnhandledRejection);
616function disableCrashOnUnhandledRejection() {
617  process.removeListener('unhandledRejection', crashOnUnhandledRejection);
618}
619
620function getTTYfd() {
621  // Do our best to grab a tty fd.
622  const tty = require('tty');
623  // Don't attempt fd 0 as it is not writable on Windows.
624  // Ref: ef2861961c3d9e9ed6972e1e84d969683b25cf95
625  const ttyFd = [1, 2, 4, 5].find(tty.isatty);
626  if (ttyFd === undefined) {
627    try {
628      return fs.openSync('/dev/tty');
629    } catch {
630      // There aren't any tty fd's available to use.
631      return -1;
632    }
633  }
634  return ttyFd;
635}
636
637function runWithInvalidFD(func) {
638  let fd = 1 << 30;
639  // Get first known bad file descriptor. 1 << 30 is usually unlikely to
640  // be an valid one.
641  try {
642    while (fs.fstatSync(fd--) && fd > 0);
643  } catch {
644    return func(fd);
645  }
646
647  printSkipMessage('Could not generate an invalid fd');
648}
649
650// A helper function to simplify checking for ERR_INVALID_ARG_TYPE output.
651function invalidArgTypeHelper(input) {
652  if (input == null) {
653    return ` Received ${input}`;
654  }
655  if (typeof input === 'function' && input.name) {
656    return ` Received function ${input.name}`;
657  }
658  if (typeof input === 'object') {
659    if (input.constructor && input.constructor.name) {
660      return ` Received an instance of ${input.constructor.name}`;
661    }
662    return ` Received ${util.inspect(input, { depth: -1 })}`;
663  }
664  let inspected = util.inspect(input, { colors: false });
665  if (inspected.length > 25)
666    inspected = `${inspected.slice(0, 25)}...`;
667  return ` Received type ${typeof input} (${inspected})`;
668}
669
670function skipIfDumbTerminal() {
671  if (isDumbTerminal) {
672    skip('skipping - dumb terminal');
673  }
674}
675
676function gcUntil(name, condition) {
677  if (typeof name === 'function') {
678    condition = name;
679    name = undefined;
680  }
681  return new Promise((resolve, reject) => {
682    let count = 0;
683    function gcAndCheck() {
684      setImmediate(() => {
685        count++;
686        global.gc();
687        if (condition()) {
688          resolve();
689        } else if (count < 10) {
690          gcAndCheck();
691        } else {
692          reject(name === undefined ? undefined : 'Test ' + name + ' failed');
693        }
694      });
695    }
696    gcAndCheck();
697  });
698}
699
700const common = {
701  allowGlobals,
702  buildType,
703  canCreateSymLink,
704  childShouldThrowAndAbort,
705  createZeroFilledFile,
706  disableCrashOnUnhandledRejection,
707  expectsError,
708  expectsInternalAssertion,
709  expectWarning,
710  gcUntil,
711  getArrayBufferViews,
712  getBufferSources,
713  getCallSite,
714  getTTYfd,
715  hasIntl,
716  hasCrypto,
717  hasMultiLocalhost,
718  invalidArgTypeHelper,
719  isAIX,
720  isAlive,
721  isDumbTerminal,
722  isFreeBSD,
723  isLinux,
724  isMainThread,
725  isOpenBSD,
726  isOSX,
727  isSunOS,
728  isWindows,
729  localIPv6Hosts,
730  mustCall,
731  mustCallAtLeast,
732  mustNotCall,
733  nodeProcessAborted,
734  PIPE,
735  platformTimeout,
736  printSkipMessage,
737  pwdCommand,
738  runWithInvalidFD,
739  skip,
740  skipIf32Bits,
741  skipIfDumbTerminal,
742  skipIfEslintMissing,
743  skipIfInspectorDisabled,
744  skipIfWorker,
745
746  get enoughTestCpu() {
747    const cpus = require('os').cpus();
748    return Array.isArray(cpus) && (cpus.length > 1 || cpus[0].speed > 999);
749  },
750
751  get enoughTestMem() {
752    return require('os').totalmem() > 0x70000000; /* 1.75 Gb */
753  },
754
755  get hasFipsCrypto() {
756    return hasCrypto && require('crypto').getFips();
757  },
758
759  get hasIPv6() {
760    const iFaces = require('os').networkInterfaces();
761    const re = isWindows ? /Loopback Pseudo-Interface/ : /lo/;
762    return Object.keys(iFaces).some((name) => {
763      return re.test(name) &&
764             iFaces[name].some(({ family }) => family === 'IPv6');
765    });
766  },
767
768  get inFreeBSDJail() {
769    if (inFreeBSDJail !== null) return inFreeBSDJail;
770
771    if (exports.isFreeBSD &&
772        execSync('sysctl -n security.jail.jailed').toString() === '1\n') {
773      inFreeBSDJail = true;
774    } else {
775      inFreeBSDJail = false;
776    }
777    return inFreeBSDJail;
778  },
779
780  // On IBMi, process.platform and os.platform() both return 'aix',
781  // It is not enough to differentiate between IBMi and real AIX system.
782  get isIBMi() {
783    return require('os').type() === 'OS400';
784  },
785
786  get isLinuxPPCBE() {
787    return (process.platform === 'linux') && (process.arch === 'ppc64') &&
788           (require('os').endianness() === 'BE');
789  },
790
791  get localhostIPv4() {
792    if (localhostIPv4 !== null) return localhostIPv4;
793
794    if (this.inFreeBSDJail) {
795      // Jailed network interfaces are a bit special - since we need to jump
796      // through loops, as well as this being an exception case, assume the
797      // user will provide this instead.
798      if (process.env.LOCALHOST) {
799        localhostIPv4 = process.env.LOCALHOST;
800      } else {
801        console.error('Looks like we\'re in a FreeBSD Jail. ' +
802                      'Please provide your default interface address ' +
803                      'as LOCALHOST or expect some tests to fail.');
804      }
805    }
806
807    if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1';
808
809    return localhostIPv4;
810  },
811
812  // opensslCli defined lazily to reduce overhead of spawnSync
813  get opensslCli() {
814    if (opensslCli !== null) return opensslCli;
815
816    if (process.config.variables.node_shared_openssl) {
817      // Use external command
818      opensslCli = 'openssl';
819    } else {
820      // Use command built from sources included in Node.js repository
821      opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli');
822    }
823
824    if (exports.isWindows) opensslCli += '.exe';
825
826    const opensslCmd = spawnSync(opensslCli, ['version']);
827    if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) {
828      // OpenSSL command cannot be executed
829      opensslCli = false;
830    }
831    return opensslCli;
832  },
833
834  get PORT() {
835    if (+process.env.TEST_PARALLEL) {
836      throw new Error('common.PORT cannot be used in a parallelized test');
837    }
838    return +process.env.NODE_COMMON_PORT || 12346;
839  }
840
841};
842
843const validProperties = new Set(Object.keys(common));
844module.exports = new Proxy(common, {
845  get(obj, prop) {
846    if (!validProperties.has(prop))
847      throw new Error(`Using invalid common property: '${prop}'`);
848    return obj[prop];
849  }
850});
851