-
Notifications
You must be signed in to change notification settings - Fork 407
Expand file tree
/
Copy pathmocha-parallel-files.spec.js
More file actions
97 lines (86 loc) · 3.11 KB
/
Copy pathmocha-parallel-files.spec.js
File metadata and controls
97 lines (86 loc) · 3.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
'use strict'
const assert = require('node:assert/strict')
const { spawn } = require('node:child_process')
const path = require('node:path')
const repoRoot = path.resolve(__dirname, '..')
const parallelScript = path.join(repoRoot, 'scripts', 'mocha-parallel-files.js')
const fixturesDir = path.join(__dirname, 'mocha-parallel-files-fixtures')
/**
* @typedef {{
* stdout: string,
* stderr: string,
* code: number|null,
* signal: keyof import('node:os').SignalConstants|null
* }} ChildResult
*
* @typedef {{
* killSignal?: keyof import('node:os').SignalConstants,
* killOnFirstStdout?: boolean,
* timeoutMs?: number
* }} RunOpts
*/
/**
* @param {string[]} args
* @param {RunOpts} [opts]
* @returns {Promise<ChildResult>}
*/
function runParallel (args, opts = {}) {
return new Promise((resolve, reject) => {
// Drop CI from the inherited env; otherwise mocha-parallel-files writes a
// junit file under the repo root for every spawn here.
const env = { ...process.env, CI: '' }
const child = spawn(process.execPath, [parallelScript, ...args], { cwd: repoRoot, env })
let stdout = ''
let stderr = ''
let killed = false
const fireKill = () => {
if (killed || !opts.killSignal) return
killed = true
child.kill(opts.killSignal)
}
child.stdout.setEncoding('utf8')
child.stderr.setEncoding('utf8')
child.stdout.on('data', (chunk) => {
stdout += chunk
// The first stdout chunk proves the parent has finished its bootstrap,
// registered SIGINT/SIGTERM handlers, and is forwarding output from a
// running child. Sending the signal earlier races the handler setup,
// which on slow CI lets the default signal terminate the parent before
// it can pin its exit code.
if (opts.killOnFirstStdout) fireKill()
})
child.stderr.on('data', (chunk) => { stderr += chunk })
const timeoutMs = opts.timeoutMs ?? 20_000
const timer = setTimeout(() => {
child.kill('SIGKILL')
reject(new Error(`mocha-parallel-files did not exit within ${timeoutMs}ms`))
}, timeoutMs)
timer.unref()
child.once('error', reject)
child.once('exit', (code, signal) => {
clearTimeout(timer)
resolve({ stdout, stderr, code, signal })
})
})
}
describe('mocha-parallel-files script', function () {
this.timeout(30_000)
it('records child stats even when the child sends an unrelated IPC payload first', async () => {
const fixture = path.join(fixturesDir, 'extra-ipc-message.js')
const { stdout, code, signal } = await runParallel(['--', fixture])
assert.strictEqual(code, 0)
assert.strictEqual(signal, null)
assert.match(stdout, /Total:\s+1\b/)
assert.match(stdout, /Passed:\s+1\b/)
assert.match(stdout, /Failed:\s+0\b/)
})
it('preserves the SIGINT exit code on user interrupt', async function () {
if (process.platform === 'win32') {
this.skip()
return
}
const fixture = path.join(fixturesDir, 'long-running.js')
const { code } = await runParallel(['--', fixture], { killSignal: 'SIGINT', killOnFirstStdout: true })
assert.strictEqual(code, 130)
})
})