Skip to content

Commit b9a822e

Browse files
alan-agius4clydin
authored andcommitted
test: avoid process kill race conditions
When using `waitForMatch` the promise is resolved prior to the process being killed which in some cases caused a race condition because `treekill` would to be invoked for a process which in the meantime will be killed gracefully.
1 parent f81dbfe commit b9a822e

File tree

1 file changed

+29
-21
lines changed

1 file changed

+29
-21
lines changed

tests/legacy-cli/e2e/utils/process.ts

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
6262
}
6363

6464
const childProcess = child_process.spawn(cmd, args, spawnOptions);
65-
// @ts-ignore
6665
childProcess.stdout!.on('data', (data: Buffer) => {
6766
stdout += data.toString('utf-8');
6867
if (options.silent) {
@@ -74,7 +73,7 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
7473
.filter((line) => line !== '')
7574
.forEach((line) => console.log(' ' + line));
7675
});
77-
// @ts-ignore
76+
7877
childProcess.stderr!.on('data', (data: Buffer) => {
7978
stderr += data.toString('utf-8');
8079
if (options.silent) {
@@ -121,14 +120,14 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
121120

122121
if (options.waitForMatch) {
123122
const match = options.waitForMatch;
124-
// @ts-ignore
123+
125124
childProcess.stdout!.on('data', (data: Buffer) => {
126125
if (data.toString().match(match)) {
127126
resolve({ stdout, stderr });
128127
matched = true;
129128
}
130129
});
131-
// @ts-ignore
130+
132131
childProcess.stderr!.on('data', (data: Buffer) => {
133132
if (data.toString().match(match)) {
134133
resolve({ stdout, stderr });
@@ -176,14 +175,14 @@ export function waitForAnyProcessOutputToMatch(
176175
new Promise((resolve) => {
177176
let stdout = '';
178177
let stderr = '';
179-
// @ts-ignore
178+
180179
childProcess.stdout!.on('data', (data: Buffer) => {
181180
stdout += data.toString();
182181
if (data.toString().match(match)) {
183182
resolve({ stdout, stderr });
184183
}
185184
});
186-
// @ts-ignore
185+
187186
childProcess.stderr!.on('data', (data: Buffer) => {
188187
stderr += data.toString();
189188
if (data.toString().match(match)) {
@@ -197,22 +196,31 @@ export function waitForAnyProcessOutputToMatch(
197196
}
198197

199198
export async function killAllProcesses(signal = 'SIGTERM'): Promise<void> {
200-
await Promise.all(
201-
_processes.map(
202-
({ pid }) =>
203-
new Promise<void>((resolve, reject) => {
204-
treeKill(pid, signal, (err) => {
205-
if (err) {
206-
reject(err);
207-
} else {
208-
resolve();
209-
}
210-
});
211-
}),
212-
),
213-
);
199+
const processesToKill: Promise<void>[] = [];
200+
201+
while (_processes.length) {
202+
const childProc = _processes.pop();
203+
if (!childProc) {
204+
continue;
205+
}
206+
207+
processesToKill.push(
208+
new Promise<void>((resolve, reject) => {
209+
treeKill(childProc.pid, signal, (err) => {
210+
if (err && !err.message.includes('not found')) {
211+
// Ignore process not found errors.
212+
// This is due to a race condition with the `waitForMatch` logic.
213+
// where promises are resolved on matches and not when the process terminates.
214+
reject(err);
215+
} else {
216+
resolve();
217+
}
218+
});
219+
}),
220+
);
221+
}
214222

215-
_processes = [];
223+
await Promise.all(processesToKill);
216224
}
217225

218226
export function exec(cmd: string, ...args: string[]) {

0 commit comments

Comments
 (0)