From cf61a04b23090f285f9351199492c4b287ce34e8 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 24 Aug 2018 12:45:31 -0700 Subject: [PATCH] test: refactor structure of common/index PR-URL: https://github.com/nodejs/node/pull/22511 Reviewed-By: Luigi Pinca Reviewed-By: Refael Ackermann Reviewed-By: Ruben Bridgewater --- test/common/index.js | 434 ++++++++++++++++++++++-------------------- test/common/index.mjs | 4 - 2 files changed, 229 insertions(+), 209 deletions(-) diff --git a/test/common/index.js b/test/common/index.js index d51cff54a6473e..ed2c28aea4c773 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -31,20 +31,15 @@ const util = require('util'); const Timer = process.binding('timer_wrap').Timer; const { fixturesDir } = require('./fixtures'); const tmpdir = require('./tmpdir'); +const { + bits, + hasIntl, + hasSmallICU +} = process.binding('config'); const noop = () => {}; -Object.defineProperty(exports, 'PORT', { - get: () => { - if (+process.env.TEST_PARALLEL) { - throw new Error('common.PORT cannot be used in a parallelized test'); - } - return +process.env.NODE_COMMON_PORT || 12346; - }, - enumerable: true -}); - -exports.isMainThread = (() => { +const isMainThread = (() => { try { return require('worker_threads').isMainThread; } catch { @@ -53,27 +48,28 @@ exports.isMainThread = (() => { } })(); -exports.isWindows = process.platform === 'win32'; -exports.isWOW64 = exports.isWindows && - (process.env.PROCESSOR_ARCHITEW6432 !== undefined); -exports.isAIX = process.platform === 'aix'; -exports.isLinuxPPCBE = (process.platform === 'linux') && - (process.arch === 'ppc64') && - (os.endianness() === 'BE'); -exports.isSunOS = process.platform === 'sunos'; -exports.isFreeBSD = process.platform === 'freebsd'; -exports.isOpenBSD = process.platform === 'openbsd'; -exports.isLinux = process.platform === 'linux'; -exports.isOSX = process.platform === 'darwin'; - -exports.enoughTestMem = os.totalmem() > 0x70000000; /* 1.75 Gb */ +const isWindows = process.platform === 'win32'; +const isWOW64 = isWindows && (process.env.PROCESSOR_ARCHITEW6432 !== undefined); +const isAIX = process.platform === 'aix'; +const isLinuxPPCBE = (process.platform === 'linux') && + (process.arch === 'ppc64') && + (os.endianness() === 'BE'); +const isSunOS = process.platform === 'sunos'; +const isFreeBSD = process.platform === 'freebsd'; +const isOpenBSD = process.platform === 'openbsd'; +const isLinux = process.platform === 'linux'; +const isOSX = process.platform === 'darwin'; + +const enoughTestMem = os.totalmem() > 0x70000000; /* 1.75 Gb */ const cpus = os.cpus(); -exports.enoughTestCpu = Array.isArray(cpus) && - (cpus.length > 1 || cpus[0].speed > 999); +const enoughTestCpu = Array.isArray(cpus) && + (cpus.length > 1 || cpus[0].speed > 999); + +const rootDir = isWindows ? 'c:\\' : '/'; -exports.rootDir = exports.isWindows ? 'c:\\' : '/'; +const buildType = process.config.target_defaults.default_configuration; -exports.buildType = process.config.target_defaults.default_configuration; +const hasCrypto = Boolean(process.versions.openssl); // If env var is set then enable async_hook hooks for all tests. if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) { @@ -126,9 +122,8 @@ let opensslCli = null; let inFreeBSDJail = null; let localhostIPv4 = null; -exports.localIPv6Hosts = ['localhost']; -if (exports.isLinux) { - exports.localIPv6Hosts = [ +const localIPv6Hosts = + isLinux ? [ // Debian/Ubuntu 'ip6-localhost', 'ip6-loopback', @@ -139,96 +134,20 @@ if (exports.isLinux) { // Typically universal 'localhost', - ]; -} - -Object.defineProperty(exports, 'inFreeBSDJail', { - get: function() { - if (inFreeBSDJail !== null) return inFreeBSDJail; - - if (exports.isFreeBSD && - execSync('sysctl -n security.jail.jailed').toString() === - '1\n') { - inFreeBSDJail = true; - } else { - inFreeBSDJail = false; - } - return inFreeBSDJail; - } -}); - -Object.defineProperty(exports, 'localhostIPv4', { - get: function() { - if (localhostIPv4 !== null) return localhostIPv4; - - if (exports.inFreeBSDJail) { - // Jailed network interfaces are a bit special - since we need to jump - // through loops, as well as this being an exception case, assume the - // user will provide this instead. - if (process.env.LOCALHOST) { - localhostIPv4 = process.env.LOCALHOST; - } else { - console.error('Looks like we\'re in a FreeBSD Jail. ' + - 'Please provide your default interface address ' + - 'as LOCALHOST or expect some tests to fail.'); - } - } - - if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1'; - - return localhostIPv4; - } -}); + ] : [ 'localhost' ]; -Object.defineProperty(exports, 'localhostIPv6', { - get: () => '::1' -}); - -// opensslCli defined lazily to reduce overhead of spawnSync -Object.defineProperty(exports, 'opensslCli', { get: function() { - if (opensslCli !== null) return opensslCli; - - if (process.config.variables.node_shared_openssl) { - // use external command - opensslCli = 'openssl'; - } else { - // use command built from sources included in Node.js repository - opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli'); - } - - if (exports.isWindows) opensslCli += '.exe'; - - const opensslCmd = spawnSync(opensslCli, ['version']); - if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) { - // openssl command cannot be executed - opensslCli = false; - } - return opensslCli; -}, enumerable: true }); - -Object.defineProperty(exports, 'hasCrypto', { - get: function() { - return Boolean(process.versions.openssl); - } -}); - -Object.defineProperty(exports, 'hasFipsCrypto', { - get: function() { - return exports.hasCrypto && require('crypto').fips; - } -}); - -{ +const PIPE = (() => { const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`); - const pipePrefix = exports.isWindows ? '\\\\.\\pipe\\' : localRelative; + const pipePrefix = isWindows ? '\\\\.\\pipe\\' : localRelative; const pipeName = `node-test.${process.pid}.sock`; - exports.PIPE = path.join(pipePrefix, pipeName); -} + return path.join(pipePrefix, pipeName); +})(); +let hasIPv6; { const iFaces = os.networkInterfaces(); - const re = exports.isWindows ? /Loopback Pseudo-Interface/ : /lo/; - exports.hasIPv6 = Object.keys(iFaces).some(function(name) { + const re = isWindows ? /Loopback Pseudo-Interface/ : /lo/; + hasIPv6 = Object.keys(iFaces).some(function(name) { return re.test(name) && iFaces[name].some(function(info) { return info.family === 'IPv6'; }); @@ -240,9 +159,9 @@ Object.defineProperty(exports, 'hasFipsCrypto', { * `$node --abort-on-uncaught-exception $file child` * the process aborts. */ -exports.childShouldThrowAndAbort = function() { +function childShouldThrowAndAbort() { let testCmd = ''; - if (!exports.isWindows) { + if (!isWindows) { // Do not create core files, as it can take a lot of disk space on // continuous testing and developers' machines testCmd += 'ulimit -c 0 && '; @@ -254,33 +173,33 @@ exports.childShouldThrowAndAbort = function() { const errMsg = 'Test should have aborted ' + `but instead exited with exit code ${exitCode}` + ` and signal ${signal}`; - assert(exports.nodeProcessAborted(exitCode, signal), errMsg); + assert(nodeProcessAborted(exitCode, signal), errMsg); }); -}; +} -exports.ddCommand = function(filename, kilobytes) { - if (exports.isWindows) { +function ddCommand(filename, kilobytes) { + if (isWindows) { const p = path.resolve(fixturesDir, 'create-file.js'); return `"${process.argv[0]}" "${p}" "${filename}" ${kilobytes * 1024}`; } else { return `dd if=/dev/zero of="${filename}" bs=1024 count=${kilobytes}`; } -}; +} -exports.pwdCommand = exports.isWindows ? +const pwdCommand = isWindows ? ['cmd.exe', ['/d', '/c', 'cd']] : ['pwd', []]; -exports.platformTimeout = function(ms) { +function platformTimeout(ms) { if (process.features.debug) ms = 2 * ms; if (global.__coverage__) ms = 4 * ms; - if (exports.isAIX) + if (isAIX) return 2 * ms; // default localhost speed is slower on AIX if (process.arch !== 'arm') @@ -295,7 +214,7 @@ exports.platformTimeout = function(ms) { return 2 * ms; // ARMv7 return ms; // ARMv8+ -}; +} let knownGlobals = [ Buffer, @@ -339,7 +258,6 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS) { function allowGlobals(...whitelist) { knownGlobals = knownGlobals.concat(whitelist); } -exports.allowGlobals = allowGlobals; function leakedGlobals() { const leaked = []; @@ -356,7 +274,6 @@ function leakedGlobals() { return leaked; } } -exports.leakedGlobals = leakedGlobals; process.on('exit', function() { const leaked = leakedGlobals(); @@ -391,19 +308,19 @@ function runCallChecks(exitCode) { if (failed.length) process.exit(1); } -exports.mustCall = function(fn, exact) { +function mustCall(fn, exact) { return _mustCallInner(fn, exact, 'exact'); -}; +} -exports.mustCallAtLeast = function(fn, minimum) { +function mustCallAtLeast(fn, minimum) { return _mustCallInner(fn, minimum, 'minimum'); -}; +} -exports.mustCallAsync = function(fn, exact) { - return exports.mustCall((...args) => { - return Promise.resolve(fn(...args)).then(exports.mustCall((val) => val)); +function mustCallAsync(fn, exact) { + return mustCall((...args) => { + return Promise.resolve(fn(...args)).then(mustCall((val) => val)); }, exact); -}; +} function _mustCallInner(fn, criteria = 1, field) { if (process._exiting) @@ -436,27 +353,27 @@ function _mustCallInner(fn, criteria = 1, field) { }; } -exports.hasMultiLocalhost = function hasMultiLocalhost() { +function hasMultiLocalhost() { const { TCP, constants: TCPConstants } = process.binding('tcp_wrap'); const t = new TCP(TCPConstants.SOCKET); const ret = t.bind('127.0.0.2', 0); t.close(); return ret === 0; -}; +} -exports.skipIfEslintMissing = function() { +function skipIfEslintMissing() { if (!fs.existsSync( path.join(__dirname, '..', '..', 'tools', 'node_modules', 'eslint') )) { - exports.skip('missing ESLint'); + skip('missing ESLint'); } -}; +} -exports.canCreateSymLink = function() { +function canCreateSymLink() { // On Windows, creating symlinks requires admin privileges. // We'll only try to run symlink test if we have enough privileges. // On other platforms, creating symlinks shouldn't need admin privileges - if (exports.isWindows) { + if (isWindows) { // whoami.exe needs to be the one from System32 // If unix tools are in the path, they can shadow the one we want, // so use the full path while executing whoami @@ -472,9 +389,9 @@ exports.canCreateSymLink = function() { } // On non-Windows platforms, this always returns `true` return true; -}; +} -exports.getCallSite = function getCallSite(top) { +function getCallSite(top) { const originalStackFormatter = Error.prepareStackTrace; Error.prepareStackTrace = (err, stack) => `${stack[0].getFileName()}:${stack[0].getLineNumber()}`; @@ -484,29 +401,29 @@ exports.getCallSite = function getCallSite(top) { err.stack; Error.prepareStackTrace = originalStackFormatter; return err.stack; -}; +} -exports.mustNotCall = function(msg) { - const callSite = exports.getCallSite(exports.mustNotCall); +function mustNotCall(msg) { + const callSite = getCallSite(mustNotCall); return function mustNotCall() { assert.fail( `${msg || 'function should not have been called'} at ${callSite}`); }; -}; +} -exports.printSkipMessage = function(msg) { +function printSkipMessage(msg) { console.log(`1..0 # Skipped: ${msg}`); -}; +} -exports.skip = function(msg) { - exports.printSkipMessage(msg); +function skip(msg) { + printSkipMessage(msg); process.exit(0); -}; +} // Returns true if the exit code "exitCode" and/or signal name "signal" // represent the exit code and/or signal name of a node process that aborted, // false otherwise. -exports.nodeProcessAborted = function nodeProcessAborted(exitCode, signal) { +function nodeProcessAborted(exitCode, signal) { // Depending on the compiler used, node will exit with either // exit code 132 (SIGILL), 133 (SIGTRAP) or 134 (SIGABRT). let expectedExitCodes = [132, 133, 134]; @@ -523,7 +440,7 @@ exports.nodeProcessAborted = function nodeProcessAborted(exitCode, signal) { // which corresponds to exit code 3221225477 (0xC0000005) // (ii) Otherwise, _exit(134) which is called in place of abort() due to // raising SIGABRT exiting with ambiguous exit code '3' by default - if (exports.isWindows) + if (isWindows) expectedExitCodes = [0xC0000005, 134]; // When using --abort-on-uncaught-exception, V8 will use @@ -538,28 +455,26 @@ exports.nodeProcessAborted = function nodeProcessAborted(exitCode, signal) { } else { return expectedExitCodes.includes(exitCode); } -}; +} -exports.busyLoop = function busyLoop(time) { +function busyLoop(time) { const startTime = Timer.now(); const stopTime = startTime + time; while (Timer.now() < stopTime) {} -}; +} -exports.isAlive = function isAlive(pid) { +function isAlive(pid) { try { process.kill(pid, 'SIGCONT'); return true; } catch (e) { return false; } -}; - -exports.noWarnCode = undefined; +} -function expectWarning(name, expected) { +function _expectWarning(name, expected) { const map = new Map(expected); - return exports.mustCall((warning) => { + return mustCall((warning) => { assert.strictEqual(warning.name, name); assert.ok(map.has(warning.message), `unexpected error message: "${warning.message}"`); @@ -575,7 +490,7 @@ function expectWarningByName(name, expected, code) { if (typeof expected === 'string') { expected = [[expected, code]]; } - process.on('warning', expectWarning(name, expected)); + process.on('warning', _expectWarning(name, expected)); } function expectWarningByMap(warningMap) { @@ -592,7 +507,7 @@ function expectWarningByMap(warningMap) { } expected = [[expected[0], expected[1]]]; } - catchWarning[name] = expectWarning(name, expected); + catchWarning[name] = _expectWarning(name, expected); }); process.on('warning', (warning) => catchWarning[warning.name](warning)); } @@ -600,25 +515,13 @@ function expectWarningByMap(warningMap) { // accepts a warning name and description or array of descriptions or a map // of warning names to description(s) // ensures a warning is generated for each name/description pair -exports.expectWarning = function(nameOrMap, expected, code) { +function expectWarning(nameOrMap, expected, code) { if (typeof nameOrMap === 'string') { expectWarningByName(nameOrMap, expected, code); } else { expectWarningByMap(nameOrMap); } -}; - -Object.defineProperty(exports, 'hasIntl', { - get: function() { - return process.binding('config').hasIntl; - } -}); - -Object.defineProperty(exports, 'hasSmallICU', { - get: function() { - return process.binding('config').hasSmallICU; - } -}); +} class Comparison { constructor(obj, keys) { @@ -630,7 +533,7 @@ class Comparison { } // Useful for testing expected internal/error objects -exports.expectsError = function expectsError(fn, settings, exact) { +function expectsError(fn, settings, exact) { if (typeof fn !== 'function') { exact = settings; settings = fn; @@ -705,26 +608,26 @@ exports.expectsError = function expectsError(fn, settings, exact) { assert.throws(fn, innerFn); return; } - return exports.mustCall(innerFn, exact); -}; + return mustCall(innerFn, exact); +} -exports.skipIfInspectorDisabled = function skipIfInspectorDisabled() { +function skipIfInspectorDisabled() { if (process.config.variables.v8_enable_inspector === 0) { - exports.skip('V8 inspector is disabled'); + skip('V8 inspector is disabled'); } - if (!exports.isMainThread) { + if (!isMainThread) { // TODO(addaleax): Fix me. - exports.skip('V8 inspector is not available in Workers'); + skip('V8 inspector is not available in Workers'); } -}; +} -exports.skipIf32Bits = function skipIf32Bits() { - if (process.binding('config').bits < 64) { - exports.skip('The tested feature is not available in 32bit builds'); +function skipIf32Bits() { + if (bits < 64) { + skip('The tested feature is not available in 32bit builds'); } -}; +} -exports.getArrayBufferViews = function getArrayBufferViews(buf) { +function getArrayBufferViews(buf) { const { buffer, byteOffset, byteLength } = buf; const out = []; @@ -749,20 +652,20 @@ exports.getArrayBufferViews = function getArrayBufferViews(buf) { } } return out; -}; +} -exports.getBufferSources = function getBufferSources(buf) { - return [...exports.getArrayBufferViews(buf), new Uint8Array(buf).buffer]; -}; +function getBufferSources(buf) { + return [...getArrayBufferViews(buf), new Uint8Array(buf).buffer]; +} // Crash the process on unhandled rejections. const crashOnUnhandledRejection = (err) => { throw err; }; process.on('unhandledRejection', crashOnUnhandledRejection); -exports.disableCrashOnUnhandledRejection = function() { +function disableCrashOnUnhandledRejection() { process.removeListener('unhandledRejection', crashOnUnhandledRejection); -}; +} -exports.getTTYfd = function getTTYfd() { +function getTTYfd() { // Do our best to grab a tty fd. const tty = require('tty'); // Don't attempt fd 0 as it is not writable on Windows. @@ -777,9 +680,9 @@ exports.getTTYfd = function getTTYfd() { } } return ttyFd; -}; +} -exports.runWithInvalidFD = function(func) { +function runWithInvalidFD(func) { let fd = 1 << 30; // Get first known bad file descriptor. 1 << 30 is usually unlikely to // be an valid one. @@ -789,5 +692,126 @@ exports.runWithInvalidFD = function(func) { return func(fd); } - exports.printSkipMessage('Could not generate an invalid fd'); + printSkipMessage('Could not generate an invalid fd'); +} + +module.exports = { + allowGlobals, + buildType, + busyLoop, + canCreateSymLink, + childShouldThrowAndAbort, + ddCommand, + disableCrashOnUnhandledRejection, + enoughTestCpu, + enoughTestMem, + expectsError, + expectWarning, + getArrayBufferViews, + getBufferSources, + getCallSite, + getTTYfd, + hasIntl, + hasCrypto, + hasIPv6, + hasSmallICU, + hasMultiLocalhost, + isAIX, + isAlive, + isFreeBSD, + isLinux, + isLinuxPPCBE, + isMainThread, + isOpenBSD, + isOSX, + isSunOS, + isWindows, + isWOW64, + leakedGlobals, + localIPv6Hosts, + mustCall, + mustCallAsync, + mustCallAtLeast, + mustNotCall, + nodeProcessAborted, + noWarnCode: undefined, + PIPE, + platformTimeout, + printSkipMessage, + pwdCommand, + rootDir, + runWithInvalidFD, + skip, + skipIf32Bits, + skipIfEslintMissing, + skipIfInspectorDisabled, + + get localhostIPv6() { return '::1'; }, + + get hasFipsCrypto() { + return hasCrypto && require('crypto').fips; + }, + + get inFreeBSDJail() { + if (inFreeBSDJail !== null) return inFreeBSDJail; + + if (exports.isFreeBSD && + execSync('sysctl -n security.jail.jailed').toString() === '1\n') { + inFreeBSDJail = true; + } else { + inFreeBSDJail = false; + } + return inFreeBSDJail; + }, + + get localhostIPv4() { + if (localhostIPv4 !== null) return localhostIPv4; + + if (this.inFreeBSDJail) { + // Jailed network interfaces are a bit special - since we need to jump + // through loops, as well as this being an exception case, assume the + // user will provide this instead. + if (process.env.LOCALHOST) { + localhostIPv4 = process.env.LOCALHOST; + } else { + console.error('Looks like we\'re in a FreeBSD Jail. ' + + 'Please provide your default interface address ' + + 'as LOCALHOST or expect some tests to fail.'); + } + } + + if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1'; + + return localhostIPv4; + }, + + // opensslCli defined lazily to reduce overhead of spawnSync + get opensslCli() { + if (opensslCli !== null) return opensslCli; + + if (process.config.variables.node_shared_openssl) { + // use external command + opensslCli = 'openssl'; + } else { + // use command built from sources included in Node.js repository + opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli'); + } + + if (exports.isWindows) opensslCli += '.exe'; + + const opensslCmd = spawnSync(opensslCli, ['version']); + if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) { + // openssl command cannot be executed + opensslCli = false; + } + return opensslCli; + }, + + get PORT() { + if (+process.env.TEST_PARALLEL) { + throw new Error('common.PORT cannot be used in a parallelized test'); + } + return +process.env.NODE_COMMON_PORT || 12346; + } + }; diff --git a/test/common/index.mjs b/test/common/index.mjs index c8e6295b5ca1c6..e0842011c663c9 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -23,8 +23,6 @@ const { hasIPv6, childShouldThrowAndAbort, ddCommand, - spawnPwd, - spawnSyncPwd, platformTimeout, allowGlobals, leakedGlobals, @@ -75,8 +73,6 @@ export { hasIPv6, childShouldThrowAndAbort, ddCommand, - spawnPwd, - spawnSyncPwd, platformTimeout, allowGlobals, leakedGlobals,