mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 20:26:08 +00:00
smoke - rewrite killing
This commit is contained in:
@@ -28,10 +28,38 @@ export interface LaunchOptions {
|
||||
browser?: 'chromium' | 'webkit' | 'firefox';
|
||||
}
|
||||
|
||||
interface ICodeInstance {
|
||||
kill: () => Promise<void>
|
||||
}
|
||||
|
||||
const instances = new Set<ICodeInstance>();
|
||||
|
||||
function registerInstance(process: cp.ChildProcess, logger: Logger, type: string, kill: () => Promise<void>) {
|
||||
const instance = { kill };
|
||||
instances.add(instance);
|
||||
process.once('exit', (code, signal) => {
|
||||
logger.log(`Process terminated (type: ${type}, pid: ${process.pid}, code: ${code}, signal: ${signal})`);
|
||||
|
||||
instances.delete(instance);
|
||||
});
|
||||
}
|
||||
|
||||
async function teardown(signal?: number) {
|
||||
stopped = true;
|
||||
|
||||
for (const instance of instances) {
|
||||
await instance.kill();
|
||||
}
|
||||
|
||||
if (typeof signal === 'number') {
|
||||
process.exit(signal);
|
||||
}
|
||||
}
|
||||
|
||||
let stopped = false;
|
||||
process.on('exit', () => stopped = true);
|
||||
process.on('SIGINT', () => stopped = true);
|
||||
process.on('SIGTERM', () => stopped = true);
|
||||
process.on('exit', () => teardown());
|
||||
process.on('SIGINT', () => teardown(128 + 2)); // https://nodejs.org/docs/v14.16.0/api/process.html#process_signal_events
|
||||
process.on('SIGTERM', () => teardown(128 + 15)); // same as above
|
||||
|
||||
export async function launch(options: LaunchOptions): Promise<Code> {
|
||||
if (stopped) {
|
||||
@@ -42,13 +70,19 @@ export async function launch(options: LaunchOptions): Promise<Code> {
|
||||
|
||||
// Browser smoke tests
|
||||
if (options.web) {
|
||||
const { serverProcess, client, driver } = await launchPlaywright(options);
|
||||
const { serverProcess, client, driver, kill } = await measureAndLog(launchPlaywright(options), 'launch playwright', options.logger);
|
||||
registerInstance(serverProcess, options.logger, 'server', kill);
|
||||
|
||||
return new Code(client, driver, options.logger, serverProcess);
|
||||
}
|
||||
|
||||
// Electron smoke tests
|
||||
const { electronProcess, client, driver } = await launchElectron(options);
|
||||
return new Code(client, driver, options.logger, electronProcess);
|
||||
else {
|
||||
const { electronProcess, client, driver, kill } = await measureAndLog(launchElectron(options), 'launch electron', options.logger);
|
||||
registerInstance(electronProcess, options.logger, 'electron', kill);
|
||||
|
||||
return new Code(client, driver, options.logger, electronProcess);
|
||||
}
|
||||
}
|
||||
|
||||
async function poll<T>(
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { LaunchOptions } from './code';
|
||||
|
||||
const repoPath = path.join(__dirname, '../../..');
|
||||
|
||||
export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess, client: IDisposable, driver: IDriver }> {
|
||||
export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess, client: IDisposable, driver: IDriver, kill: () => Promise<void> }> {
|
||||
const { codePath, workspacePath, extensionsPath, userDataDir, remote, logger, verbose, extraArgs } = options;
|
||||
const env = { ...process.env };
|
||||
const logsPath = path.join(repoPath, '.build', 'logs', remote ? 'smoke-tests-remote' : 'smoke-tests');
|
||||
@@ -89,30 +89,30 @@ export async function launch(options: LaunchOptions): Promise<{ electronProcess:
|
||||
const electronPath = codePath ? getBuildElectronPath(codePath) : getDevElectronPath();
|
||||
const electronProcess = spawn(electronPath, args, spawnOptions);
|
||||
|
||||
if (verbose) {
|
||||
logger.log(`Started electron for desktop smoke tests on pid ${electronProcess.pid}`);
|
||||
}
|
||||
|
||||
let electronProcessDidExit = false;
|
||||
electronProcess.once('exit', (code, signal) => {
|
||||
if (verbose) {
|
||||
logger.log(`Electron for desktop smoke tests terminated (pid: ${electronProcess.pid}, code: ${code}, signal: ${signal})`);
|
||||
}
|
||||
electronProcessDidExit = true;
|
||||
});
|
||||
|
||||
process.once('exit', () => {
|
||||
if (!electronProcessDidExit) {
|
||||
electronProcess.kill();
|
||||
}
|
||||
});
|
||||
logger.log(`Started electron for desktop smoke tests on pid ${electronProcess.pid}`);
|
||||
|
||||
let retries = 0;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const { client, driver } = await measureAndLog(connectElectronDriver(outPath, driverIPCHandle), 'connectElectronDriver()', logger);
|
||||
return { electronProcess, client, driver };
|
||||
return {
|
||||
electronProcess,
|
||||
client,
|
||||
driver,
|
||||
kill: async () => {
|
||||
try {
|
||||
return promisify(kill)(electronProcess.pid!);
|
||||
} catch (error) {
|
||||
try {
|
||||
process.kill(electronProcess.pid!, 0); // throws an exception if the process doesn't exist anymore
|
||||
logger.log(`Error tearing down electron client (pid: ${electronProcess.pid}): ${error}`);
|
||||
} catch (error) {
|
||||
return; // Expected when process is gone
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
|
||||
// give up
|
||||
|
||||
@@ -199,7 +199,7 @@ class PlaywrightDriver implements IDriver {
|
||||
|
||||
let port = 9000;
|
||||
|
||||
export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess, client: IDisposable, driver: IDriver }> {
|
||||
export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess, client: IDisposable, driver: IDriver, kill: () => Promise<void> }> {
|
||||
|
||||
// Launch server
|
||||
const { serverProcess, endpoint } = await launchServer(options);
|
||||
@@ -212,12 +212,13 @@ export async function launch(options: LaunchOptions): Promise<{ serverProcess: C
|
||||
client: {
|
||||
dispose: () => { /* there is no client to dispose for browser, teardown is triggered via exitApplication call */ }
|
||||
},
|
||||
driver: new PlaywrightDriver(serverProcess, browser, context, page, options.logger)
|
||||
driver: new PlaywrightDriver(serverProcess, browser, context, page, options.logger),
|
||||
kill: () => teardown(serverProcess, options.logger)
|
||||
};
|
||||
}
|
||||
|
||||
async function launchServer(options: LaunchOptions) {
|
||||
const { userDataDir, codePath, extensionsPath, verbose, logger } = options;
|
||||
const { userDataDir, codePath, extensionsPath, logger } = options;
|
||||
const codeServerPath = codePath ?? process.env.VSCODE_REMOTE_SERVER_PATH;
|
||||
const agentFolder = userDataDir;
|
||||
await measureAndLog(promisify(mkdir)(agentFolder), `mkdir(${agentFolder})`, logger);
|
||||
@@ -234,18 +235,14 @@ async function launchServer(options: LaunchOptions) {
|
||||
serverLocation = join(codeServerPath, `server.${process.platform === 'win32' ? 'cmd' : 'sh'}`);
|
||||
args.push(`--logsPath=${logsPath}`);
|
||||
|
||||
if (verbose) {
|
||||
logger.log(`Starting built server from '${serverLocation}'`);
|
||||
logger.log(`Storing log files into '${logsPath}'`);
|
||||
}
|
||||
logger.log(`Starting built server from '${serverLocation}'`);
|
||||
logger.log(`Storing log files into '${logsPath}'`);
|
||||
} else {
|
||||
serverLocation = join(root, `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`);
|
||||
args.push('--logsPath', logsPath);
|
||||
|
||||
if (verbose) {
|
||||
logger.log(`Starting server out of sources from '${serverLocation}'`);
|
||||
logger.log(`Storing log files into '${logsPath}'`);
|
||||
}
|
||||
logger.log(`Starting server out of sources from '${serverLocation}'`);
|
||||
logger.log(`Storing log files into '${logsPath}'`);
|
||||
}
|
||||
|
||||
const serverProcess = spawn(
|
||||
@@ -254,17 +251,11 @@ async function launchServer(options: LaunchOptions) {
|
||||
{ env }
|
||||
);
|
||||
|
||||
if (verbose) {
|
||||
logger.log(`*** Started server for browser smoke tests (pid: ${serverProcess.pid})`);
|
||||
serverProcess.once('exit', (code, signal) => logger.log(`Server for browser smoke tests terminated (pid: ${serverProcess.pid}, code: ${code}, signal: ${signal})`));
|
||||
logger.log(`Started server for browser smoke tests (pid: ${serverProcess.pid})`);
|
||||
serverProcess.once('exit', (code, signal) => logger.log(`Server for browser smoke tests terminated (pid: ${serverProcess.pid}, code: ${code}, signal: ${signal})`));
|
||||
|
||||
serverProcess.stderr?.on('data', error => logger.log(`Server stderr: ${error}`));
|
||||
serverProcess.stdout?.on('data', data => logger.log(`Server stdout: ${data}`));
|
||||
}
|
||||
|
||||
process.on('exit', () => teardown(serverProcess, logger));
|
||||
process.on('SIGINT', () => teardown(serverProcess, logger));
|
||||
process.on('SIGTERM', () => teardown(serverProcess, logger));
|
||||
serverProcess.stderr?.on('data', error => logger.log(`Server stderr: ${error}`));
|
||||
serverProcess.stdout?.on('data', data => logger.log(`Server stdout: ${data}`));
|
||||
|
||||
return {
|
||||
serverProcess,
|
||||
|
||||
@@ -162,8 +162,14 @@ async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.U
|
||||
}
|
||||
|
||||
process.on('exit', () => serverProcess.kill());
|
||||
process.on('SIGINT', () => serverProcess.kill());
|
||||
process.on('SIGTERM', () => serverProcess.kill());
|
||||
process.on('SIGINT', () => {
|
||||
serverProcess.kill();
|
||||
process.exit(128 + 2); // https://nodejs.org/docs/v14.16.0/api/process.html#process_signal_events
|
||||
});
|
||||
process.on('SIGTERM', () => {
|
||||
serverProcess.kill();
|
||||
process.exit(128 + 15); // https://nodejs.org/docs/v14.16.0/api/process.html#process_signal_events
|
||||
});
|
||||
|
||||
return new Promise(c => {
|
||||
serverProcess.stdout!.on('data', data => {
|
||||
|
||||
Reference in New Issue
Block a user