1'use strict'; 2 3const assert = require('assert'); 4const common = require('./'); 5const util = require('util'); 6 7// Workaround for Windows Server 2008R2 8// When CMD is used to launch a process and CMD is killed too quickly, the 9// process can stay behind running in suspended state, never completing. 10function cleanupStaleProcess(filename) { 11 if (!common.isWindows) { 12 return; 13 } 14 process.once('beforeExit', () => { 15 const basename = filename.replace(/.*[/\\]/g, ''); 16 try { 17 require('child_process') 18 .execFileSync(`${process.env.SystemRoot}\\System32\\wbem\\WMIC.exe`, [ 19 'process', 20 'where', 21 `commandline like '%${basename}%child'`, 22 'delete', 23 '/nointeractive', 24 ]); 25 } catch { 26 // Ignore failures, there might not be any stale process to clean up. 27 } 28 }); 29} 30 31// This should keep the child process running long enough to expire 32// the timeout. 33const kExpiringChildRunTime = common.platformTimeout(20 * 1000); 34const kExpiringParentTimer = 1; 35assert(kExpiringChildRunTime > kExpiringParentTimer); 36 37function logAfterTime(time) { 38 setTimeout(() => { 39 // The following console statements are part of the test. 40 console.log('child stdout'); 41 console.error('child stderr'); 42 }, time); 43} 44 45function checkOutput(str, check) { 46 if ((check instanceof RegExp && !check.test(str)) || 47 (typeof check === 'string' && check !== str)) { 48 return { passed: false, reason: `did not match ${util.inspect(check)}` }; 49 } 50 if (typeof check === 'function') { 51 try { 52 check(str); 53 } catch (error) { 54 return { 55 passed: false, 56 reason: `did not match expectation, checker throws:\n${util.inspect(error)}`, 57 }; 58 } 59 } 60 return { passed: true }; 61} 62 63function expectSyncExit(child, { 64 status, 65 signal, 66 stderr: stderrCheck, 67 stdout: stdoutCheck, 68 trim = false, 69}) { 70 const failures = []; 71 let stderrStr, stdoutStr; 72 if (status !== undefined && child.status !== status) { 73 failures.push(`- process terminated with status ${child.status}, expected ${status}`); 74 } 75 if (signal !== undefined && child.signal !== signal) { 76 failures.push(`- process terminated with signal ${child.signal}, expected ${signal}`); 77 } 78 79 function logAndThrow() { 80 const tag = `[process ${child.pid}]:`; 81 console.error(`${tag} --- stderr ---`); 82 console.error(stderrStr === undefined ? child.stderr.toString() : stderrStr); 83 console.error(`${tag} --- stdout ---`); 84 console.error(stdoutStr === undefined ? child.stdout.toString() : stdoutStr); 85 console.error(`${tag} status = ${child.status}, signal = ${child.signal}`); 86 throw new Error(`${failures.join('\n')}`); 87 } 88 89 // If status and signal are not matching expectations, fail early. 90 if (failures.length !== 0) { 91 logAndThrow(); 92 } 93 94 if (stderrCheck !== undefined) { 95 stderrStr = child.stderr.toString(); 96 const { passed, reason } = checkOutput(trim ? stderrStr.trim() : stderrStr, stderrCheck); 97 if (!passed) { 98 failures.push(`- stderr ${reason}`); 99 } 100 } 101 if (stdoutCheck !== undefined) { 102 stdoutStr = child.stdout.toString(); 103 const { passed, reason } = checkOutput(trim ? stdoutStr.trim() : stdoutStr, stdoutCheck); 104 if (!passed) { 105 failures.push(`- stdout ${reason}`); 106 } 107 } 108 if (failures.length !== 0) { 109 logAndThrow(); 110 } 111 return { child, stderr: stderrStr, stdout: stdoutStr }; 112} 113 114function expectSyncExitWithoutError(child, options) { 115 return expectSyncExit(child, { 116 status: 0, 117 signal: null, 118 ...options, 119 }); 120} 121 122module.exports = { 123 cleanupStaleProcess, 124 logAfterTime, 125 kExpiringChildRunTime, 126 kExpiringParentTimer, 127 expectSyncExit, 128 expectSyncExitWithoutError, 129}; 130