diff options
Diffstat (limited to 'tests/node_compat/test/common')
-rw-r--r-- | tests/node_compat/test/common/child_process.js | 106 | ||||
-rw-r--r-- | tests/node_compat/test/common/index.js | 33 | ||||
-rw-r--r-- | tests/node_compat/test/common/index.mjs | 132 | ||||
-rw-r--r-- | tests/node_compat/test/common/tmpdir.js | 39 |
4 files changed, 228 insertions, 82 deletions
diff --git a/tests/node_compat/test/common/child_process.js b/tests/node_compat/test/common/child_process.js index d46ec39d1..e30ec0c00 100644 --- a/tests/node_compat/test/common/child_process.js +++ b/tests/node_compat/test/common/child_process.js @@ -8,7 +8,9 @@ 'use strict'; const assert = require('assert'); +const { spawnSync, execFileSync } = require('child_process'); const common = require('./'); +const util = require('util'); // Workaround for Windows Server 2008R2 // When CMD is used to launch a process and CMD is killed too quickly, the @@ -20,14 +22,13 @@ function cleanupStaleProcess(filename) { process.once('beforeExit', () => { const basename = filename.replace(/.*[/\\]/g, ''); try { - require('child_process') - .execFileSync(`${process.env.SystemRoot}\\System32\\wbem\\WMIC.exe`, [ - 'process', - 'where', - `commandline like '%${basename}%child'`, - 'delete', - '/nointeractive', - ]); + execFileSync(`${process.env.SystemRoot}\\System32\\wbem\\WMIC.exe`, [ + 'process', + 'where', + `commandline like '%${basename}%child'`, + 'delete', + '/nointeractive', + ]); } catch { // Ignore failures, there might not be any stale process to clean up. } @@ -48,9 +49,98 @@ function logAfterTime(time) { }, time); } +function checkOutput(str, check) { + if ((check instanceof RegExp && !check.test(str)) || + (typeof check === 'string' && check !== str)) { + return { passed: false, reason: `did not match ${util.inspect(check)}` }; + } + if (typeof check === 'function') { + try { + check(str); + } catch (error) { + return { + passed: false, + reason: `did not match expectation, checker throws:\n${util.inspect(error)}`, + }; + } + } + return { passed: true }; +} + +function expectSyncExit(child, { + status, + signal, + stderr: stderrCheck, + stdout: stdoutCheck, + trim = false, +}) { + const failures = []; + let stderrStr, stdoutStr; + if (status !== undefined && child.status !== status) { + failures.push(`- process terminated with status ${child.status}, expected ${status}`); + } + if (signal !== undefined && child.signal !== signal) { + failures.push(`- process terminated with signal ${child.signal}, expected ${signal}`); + } + + function logAndThrow() { + const tag = `[process ${child.pid}]:`; + console.error(`${tag} --- stderr ---`); + console.error(stderrStr === undefined ? child.stderr.toString() : stderrStr); + console.error(`${tag} --- stdout ---`); + console.error(stdoutStr === undefined ? child.stdout.toString() : stdoutStr); + console.error(`${tag} status = ${child.status}, signal = ${child.signal}`); + throw new Error(`${failures.join('\n')}`); + } + + // If status and signal are not matching expectations, fail early. + if (failures.length !== 0) { + logAndThrow(); + } + + if (stderrCheck !== undefined) { + stderrStr = child.stderr.toString(); + const { passed, reason } = checkOutput(trim ? stderrStr.trim() : stderrStr, stderrCheck); + if (!passed) { + failures.push(`- stderr ${reason}`); + } + } + if (stdoutCheck !== undefined) { + stdoutStr = child.stdout.toString(); + const { passed, reason } = checkOutput(trim ? stdoutStr.trim() : stdoutStr, stdoutCheck); + if (!passed) { + failures.push(`- stdout ${reason}`); + } + } + if (failures.length !== 0) { + logAndThrow(); + } + return { child, stderr: stderrStr, stdout: stdoutStr }; +} + +function spawnSyncAndExit(...args) { + const spawnArgs = args.slice(0, args.length - 1); + const expectations = args[args.length - 1]; + const child = spawnSync(...spawnArgs); + return expectSyncExit(child, expectations); +} + +function spawnSyncAndExitWithoutError(...args) { + const spawnArgs = args.slice(0, args.length); + const expectations = args[args.length - 1]; + const child = spawnSync(...spawnArgs); + return expectSyncExit(child, { + status: 0, + signal: null, + ...expectations, + }); +} + module.exports = { cleanupStaleProcess, logAfterTime, kExpiringChildRunTime, kExpiringParentTimer, + spawnSyncAndExit, + spawnSyncAndExitWithoutError, }; diff --git a/tests/node_compat/test/common/index.js b/tests/node_compat/test/common/index.js index 9f5b4814c..2ebb22da2 100644 --- a/tests/node_compat/test/common/index.js +++ b/tests/node_compat/test/common/index.js @@ -11,10 +11,12 @@ */ 'use strict'; const assert = require("assert"); +const { spawn } = require('child_process'); const path = require("path"); const util = require("util"); const tmpdir = require("./tmpdir"); + function platformTimeout(ms) { return ms; } @@ -442,6 +444,36 @@ const pwdCommand = isWindows ? ['cmd.exe', ['/d', '/c', 'cd']] : ['pwd', []]; + function spawnPromisified(...args) { + let stderr = ''; + let stdout = ''; + + const child = spawn(...args); + child.stderr.setEncoding('utf8'); + child.stderr.on('data', (data) => { stderr += data; }); + child.stdout.setEncoding('utf8'); + child.stdout.on('data', (data) => { stdout += data; }); + + return new Promise((resolve, reject) => { + child.on('close', (code, signal) => { + resolve({ + code, + signal, + stderr, + stdout, + }); + }); + child.on('error', (code, signal) => { + reject({ + code, + signal, + stderr, + stdout, + }); + }); + }); + } + module.exports = { allowGlobals, expectsError, @@ -464,6 +496,7 @@ module.exports = { printSkipMessage, pwdCommand, skipIfDumbTerminal, + spawnPromisified, isDumbTerminal, isWindows, isAIX, diff --git a/tests/node_compat/test/common/index.mjs b/tests/node_compat/test/common/index.mjs index d5473eaea..d9992e67d 100644 --- a/tests/node_compat/test/common/index.mjs +++ b/tests/node_compat/test/common/index.mjs @@ -11,105 +11,105 @@ const require = createRequire(import.meta.url); const common = require('./index.js'); const { - isMainThread, - isWindows, + allowGlobals, + buildType, + canCreateSymLink, + checkoutEOL, + childShouldThrowAndAbort, + createZeroFilledFile, + enoughTestMem, + expectsError, + expectWarning, + getArrayBufferViews, + getBufferSources, + getCallSite, + getTTYfd, + hasCrypto, + hasIPv6, + hasMultiLocalhost, isAIX, - isIBMi, - isLinuxPPCBE, - isSunOS, + isAlive, isDumbTerminal, isFreeBSD, - isOpenBSD, + isIBMi, isLinux, + isLinuxPPCBE, + isMainThread, + isOpenBSD, isOSX, - enoughTestMem, - buildType, + isSunOS, + isWindows, localIPv6Hosts, - opensslCli, - PIPE, - hasCrypto, - hasIPv6, - childShouldThrowAndAbort, - checkoutEOL, - createZeroFilledFile, - platformTimeout, - allowGlobals, mustCall, mustCallAtLeast, - mustSucceed, - hasMultiLocalhost, - skipIfDumbTerminal, - skipIfEslintMissing, - canCreateSymLink, - getCallSite, mustNotCall, mustNotMutateObjectDeep, + mustSucceed, + nodeProcessAborted, + opensslCli, parseTestFlags, + PIPE, + platformTimeout, printSkipMessage, + runWithInvalidFD, skip, - nodeProcessAborted, - isAlive, - expectWarning, - expectsError, - skipIfInspectorDisabled, skipIf32Bits, - getArrayBufferViews, - getBufferSources, - getTTYfd, - runWithInvalidFD, + skipIfDumbTerminal, + skipIfEslintMissing, + skipIfInspectorDisabled, spawnPromisified, } = common; const getPort = () => common.PORT; export { - isMainThread, - isWindows, + allowGlobals, + buildType, + canCreateSymLink, + checkoutEOL, + childShouldThrowAndAbort, + createRequire, + createZeroFilledFile, + enoughTestMem, + expectsError, + expectWarning, + getArrayBufferViews, + getBufferSources, + getCallSite, + getPort, + getTTYfd, + hasCrypto, + hasIPv6, + hasMultiLocalhost, isAIX, - isIBMi, - isLinuxPPCBE, - isSunOS, + isAlive, isDumbTerminal, isFreeBSD, - isOpenBSD, + isIBMi, isLinux, + isLinuxPPCBE, + isMainThread, + isOpenBSD, isOSX, - enoughTestMem, - buildType, + isSunOS, + isWindows, localIPv6Hosts, - opensslCli, - PIPE, - hasCrypto, - hasIPv6, - childShouldThrowAndAbort, - checkoutEOL, - createZeroFilledFile, - platformTimeout, - allowGlobals, mustCall, mustCallAtLeast, - mustSucceed, - hasMultiLocalhost, - skipIfDumbTerminal, - skipIfEslintMissing, - canCreateSymLink, - getCallSite, mustNotCall, mustNotMutateObjectDeep, + mustSucceed, + nodeProcessAborted, + opensslCli, parseTestFlags, + PIPE, + platformTimeout, printSkipMessage, + runWithInvalidFD, skip, - nodeProcessAborted, - isAlive, - expectWarning, - expectsError, - skipIfInspectorDisabled, skipIf32Bits, - getArrayBufferViews, - getBufferSources, - getTTYfd, - runWithInvalidFD, - createRequire, + skipIfDumbTerminal, + skipIfEslintMissing, + skipIfInspectorDisabled, spawnPromisified, - getPort, }; diff --git a/tests/node_compat/test/common/tmpdir.js b/tests/node_compat/test/common/tmpdir.js index 668424cdc..9315b8747 100644 --- a/tests/node_compat/test/common/tmpdir.js +++ b/tests/node_compat/test/common/tmpdir.js @@ -7,12 +7,25 @@ 'use strict'; +const { spawnSync } = require('child_process'); const fs = require('fs'); const path = require('path'); +const { pathToFileURL } = require('url'); const { isMainThread } = require('worker_threads'); -function rmSync(pathname) { - fs.rmSync(pathname, { maxRetries: 3, recursive: true, force: true }); +function rmSync(pathname, useSpawn) { + if (useSpawn) { + const escapedPath = pathname.replaceAll('\\', '\\\\'); + spawnSync( + process.execPath, + [ + '-e', + `require("fs").rmSync("${escapedPath}", { maxRetries: 3, recursive: true, force: true });`, + ], + ); + } else { + fs.rmSync(pathname, { maxRetries: 3, recursive: true, force: true }); + } } const testRoot = process.env.NODE_TEST_DIR ? @@ -25,25 +38,27 @@ const tmpdirName = '.tmp.' + const tmpPath = path.join(testRoot, tmpdirName); let firstRefresh = true; -function refresh() { - rmSync(tmpPath); +function refresh(useSpawn = false) { + rmSync(tmpPath, useSpawn); fs.mkdirSync(tmpPath); if (firstRefresh) { firstRefresh = false; // Clean only when a test uses refresh. This allows for child processes to // use the tmpdir and only the parent will clean on exit. - process.on('exit', onexit); + process.on('exit', () => { + return onexit(useSpawn); + }); } } -function onexit() { +function onexit(useSpawn) { // Change directory to avoid possible EBUSY if (isMainThread) process.chdir(testRoot); try { - rmSync(tmpPath); + rmSync(tmpPath, useSpawn); } catch (e) { console.error('Can\'t clean tmpdir:', tmpPath); @@ -71,9 +86,17 @@ function hasEnoughSpace(size) { return bavail >= Math.ceil(size / bsize); } +function fileURL(...paths) { + // When called without arguments, add explicit trailing slash + const fullPath = path.resolve(tmpPath + path.sep, ...paths); + + return pathToFileURL(fullPath); +} + module.exports = { + fileURL, + hasEnoughSpace, path: tmpPath, refresh, - hasEnoughSpace, resolve, }; |