• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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