make it work correctly on linux
This commit is contained in:
parent
a30a3bbe9d
commit
6d1dfe18a0
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
|
import child_process from 'child_process'
|
||||||
|
|
||||||
import { colors, debug } from 'playwright-core/lib/utilsBundle';
|
import { colors, debug } from 'playwright-core/lib/utilsBundle';
|
||||||
import { raceAgainstDeadline, launchProcess, monotonicTime, isURLAvailable } from 'playwright-core/lib/utils';
|
import { raceAgainstDeadline, launchProcess, monotonicTime, isURLAvailable } from 'playwright-core/lib/utils';
|
||||||
|
|
@ -124,10 +125,18 @@ export class WebServerPlugin implements TestRunnerPlugin {
|
||||||
if (!signal)
|
if (!signal)
|
||||||
throw new Error('skip graceful shutdown');
|
throw new Error('skip graceful shutdown');
|
||||||
|
|
||||||
// launchedProcess is a shell. not all shells forward signals to their child processes.
|
if (signal === "SIGINT") // proper usage of SIGINT is to send it to the entire process group, see https://www.cons.org/cracauer/sigint.html
|
||||||
// we need to send the signal to the webserver inside the shell.
|
process.kill(-launchedProcess.pid!, "SIGINT");
|
||||||
const webserverPid = launchedProcess.pid!; // TODO: get the actual pid of the webserver
|
else { // SIGTERM is sent to the top process only, which then decides what to do
|
||||||
process.kill(webserverPid, signal);
|
let pid = launchedProcess.pid!;
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
for (const webServerPID of this._queryChildProcesses(launchedProcess.pid!))
|
||||||
|
process.kill(webServerPID, 'SIGTERM');
|
||||||
|
} else {
|
||||||
|
process.kill(-pid, "SIGTERM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const timer = timeout !== 0
|
const timer = timeout !== 0
|
||||||
? setTimeout(() => reject(new Error(`process didn't close gracefully within timeout, falling back to SIGKILL`)), timeout)
|
? setTimeout(() => reject(new Error(`process didn't close gracefully within timeout, falling back to SIGKILL`)), timeout)
|
||||||
|
|
@ -156,6 +165,11 @@ export class WebServerPlugin implements TestRunnerPlugin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _queryChildProcesses(parentPID: number): number[] {
|
||||||
|
const output = child_process.execSync(`ps --ppid ${parentPID} -o pid`, { encoding: 'utf-8' });
|
||||||
|
return output.split("\n").map(l => parseInt(l.trim(), 10)).filter(l => !Number.isNaN(l))
|
||||||
|
}
|
||||||
|
|
||||||
private async _waitForProcess() {
|
private async _waitForProcess() {
|
||||||
if (!this._isAvailableCallback) {
|
if (!this._isAvailableCallback) {
|
||||||
this._processExitedPromise.catch(() => {});
|
this._processExitedPromise.catch(() => {});
|
||||||
|
|
|
||||||
|
|
@ -752,10 +752,23 @@ test.describe('kill option', () => {
|
||||||
const files = (additionalOptions = {}) => {
|
const files = (additionalOptions = {}) => {
|
||||||
const port = test.info().workerIndex * 2 + 10510;
|
const port = test.info().workerIndex * 2 + 10510;
|
||||||
return {
|
return {
|
||||||
|
'child.js': `
|
||||||
|
process.on('SIGINT', () => { console.log('%%childprocess received SIGINT'); setTimeout(() => process.exit(), 10) })
|
||||||
|
process.on('SIGTERM', () => { console.log('%%childprocess received SIGTERM'); setTimeout(() => process.exit(), 10) })
|
||||||
|
process.on('message', msg => console.log(msg))
|
||||||
|
`,
|
||||||
'web-server.js': `
|
'web-server.js': `
|
||||||
process.on('SIGINT', () => { console.log('%%webserver received SIGINT but stubbornly refuses to wind down') })
|
const child = require("node:child_process").fork('./child.js', { silent: false })
|
||||||
process.on('SIGTERM', () => { console.log('%%webserver received SIGTERM but stubbornly refuses to wind down') })
|
|
||||||
const server = require('http').createServer((req, res) => { res.end("ok"); })
|
process.on('SIGINT', () => {
|
||||||
|
console.log('%%webserver received SIGINT but stubbornly refuses to wind down')
|
||||||
|
})
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
console.log('%%webserver received SIGTERM but stubbornly refuses to wind down')
|
||||||
|
child.kill('SIGINT')
|
||||||
|
})
|
||||||
|
|
||||||
|
const server = require("node:http").createServer((req, res) => { res.end("ok"); })
|
||||||
server.listen(process.argv[2]);
|
server.listen(process.argv[2]);
|
||||||
`,
|
`,
|
||||||
'test.spec.ts': `
|
'test.spec.ts': `
|
||||||
|
|
@ -765,7 +778,7 @@ test.describe('kill option', () => {
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
module.exports = {
|
module.exports = {
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'node web-server.js ${port}',
|
command: 'echo some-precondition && node web-server.js ${port}',
|
||||||
port: ${port},
|
port: ${port},
|
||||||
stdout: 'pipe',
|
stdout: 'pipe',
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
|
|
@ -788,12 +801,12 @@ test.describe('kill option', () => {
|
||||||
|
|
||||||
test('can be configured to send SIGTERM', async ({ runInlineTest }) => {
|
test('can be configured to send SIGTERM', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest(files({ kill: { SIGTERM: 500 } }), { workers: 1 });
|
const result = await runInlineTest(files({ kill: { SIGTERM: 500 } }), { workers: 1 });
|
||||||
expect(parseOutputLines(result)).toEqual(['webserver received SIGTERM but stubbornly refuses to wind down']);
|
expect(parseOutputLines(result)).toEqual(['webserver received SIGTERM but stubbornly refuses to wind down', 'childprocess received SIGINT']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can be configured to send SIGINT', async ({ runInlineTest }) => {
|
test('can be configured to send SIGINT', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest(files({ kill: { SIGINT: 500 } }), { workers: 1 });
|
const result = await runInlineTest(files({ kill: { SIGINT: 5000 } }), { workers: 1 });
|
||||||
expect(parseOutputLines(result)).toEqual(['webserver received SIGINT but stubbornly refuses to wind down']);
|
expect(parseOutputLines(result).sort()).toEqual(['childprocess received SIGINT', 'webserver received SIGINT but stubbornly refuses to wind down']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws when mixed', async ({ runInlineTest }) => {
|
test('throws when mixed', async ({ runInlineTest }) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue