fix(test-runner): don't double emit SIGINT in long running test (#24265)
https://github.com/microsoft/playwright/issues/23907 This fixes the following scenario. You press Command + C during the test-execution, then a global teardown will execute, which takes a long time and before this on macOS did sometimes end up in an unwanted SIGINT received inside the globalTeardown handler. After this change this won't happen anymore.
This commit is contained in:
Родитель
0b2516d71a
Коммит
d2f581a19c
|
@ -22,25 +22,11 @@ export class SigIntWatcher {
|
||||||
let sigintCallback: () => void;
|
let sigintCallback: () => void;
|
||||||
this._sigintPromise = new Promise<void>(f => sigintCallback = f);
|
this._sigintPromise = new Promise<void>(f => sigintCallback = f);
|
||||||
this._sigintHandler = () => {
|
this._sigintHandler = () => {
|
||||||
// We remove the handler so that second Ctrl+C immediately kills the runner
|
FixedNodeSIGINTHandler.off(this._sigintHandler);
|
||||||
// via the default sigint handler. This is handy in the case where our shutdown
|
|
||||||
// takes a lot of time or is buggy.
|
|
||||||
//
|
|
||||||
// When running through NPM we might get multiple SIGINT signals
|
|
||||||
// for a single Ctrl+C - this is an NPM bug present since at least NPM v6.
|
|
||||||
// https://github.com/npm/cli/issues/1591
|
|
||||||
// https://github.com/npm/cli/issues/2124
|
|
||||||
//
|
|
||||||
// Therefore, removing the handler too soon will just kill the process
|
|
||||||
// with default handler without printing the results.
|
|
||||||
// We work around this by giving NPM 1000ms to send us duplicate signals.
|
|
||||||
// The side effect is that slow shutdown or bug in our runner will force
|
|
||||||
// the user to hit Ctrl+C again after at least a second.
|
|
||||||
setTimeout(() => process.off('SIGINT', this._sigintHandler), 1000);
|
|
||||||
this._hadSignal = true;
|
this._hadSignal = true;
|
||||||
sigintCallback();
|
sigintCallback();
|
||||||
};
|
};
|
||||||
process.on('SIGINT', this._sigintHandler);
|
FixedNodeSIGINTHandler.on(this._sigintHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise(): Promise<void> {
|
promise(): Promise<void> {
|
||||||
|
@ -52,6 +38,53 @@ export class SigIntWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
disarm() {
|
disarm() {
|
||||||
process.off('SIGINT', this._sigintHandler);
|
FixedNodeSIGINTHandler.off(this._sigintHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NPM/NPX will send us duplicate SIGINT signals, so we need to ignore them.
|
||||||
|
class FixedNodeSIGINTHandler {
|
||||||
|
private static _handlers: (() => void)[] = [];
|
||||||
|
private static _ignoreNextSIGINTs = false;
|
||||||
|
|
||||||
|
static _dispatch = () => {
|
||||||
|
if (this._ignoreNextSIGINTs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._ignoreNextSIGINTs = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this._ignoreNextSIGINTs = false;
|
||||||
|
// We remove the handler so that second Ctrl+C immediately kills the process
|
||||||
|
// via the default sigint handler. This is handy in the case where our shutdown
|
||||||
|
// takes a lot of time or is buggy.
|
||||||
|
//
|
||||||
|
// When running through NPM we might get multiple SIGINT signals
|
||||||
|
// for a single Ctrl+C - this is an NPM bug present since NPM v6+.
|
||||||
|
// https://github.com/npm/cli/issues/1591
|
||||||
|
// https://github.com/npm/cli/issues/2124
|
||||||
|
// https://github.com/npm/cli/issues/5021
|
||||||
|
//
|
||||||
|
// Therefore, removing the handler too soon will just kill the process
|
||||||
|
// with default handler without printing the results.
|
||||||
|
// We work around this by giving NPM 1000ms to send us duplicate signals.
|
||||||
|
// The side effect is that slow shutdown or bug in our process will force
|
||||||
|
// the user to hit Ctrl+C again after at least a second.
|
||||||
|
if (!this._handlers.length)
|
||||||
|
process.off('SIGINT', this._dispatch);
|
||||||
|
}, 1000);
|
||||||
|
for (const handler of this._handlers)
|
||||||
|
handler();
|
||||||
|
};
|
||||||
|
|
||||||
|
static on(handler: () => void) {
|
||||||
|
this._handlers.push(handler);
|
||||||
|
if (this._handlers.length === 1)
|
||||||
|
process.on('SIGINT', this._dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static off(handler: () => void) {
|
||||||
|
this._handlers = this._handlers.filter(h => h !== handler);
|
||||||
|
if (!this._ignoreNextSIGINTs && !this._handlers.length)
|
||||||
|
process.off('SIGINT', this._dispatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче