Path: blob/main/test/smoke/src/areas/extensions/extension-host-restart.test.ts
5344 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as fs from 'fs';6import * as path from 'path';7import { Application, Logger } from '../../../../automation';8import { installAllHandlers, timeout } from '../../utils';910/**11* Verifies that window reload kills the extension host even when blocked.12*13*/14export function setup(logger: Logger) {15describe('Extension Host Restart', () => {1617installAllHandlers(logger, opts => opts);1819function processExists(pid: number): boolean {20try {21process.kill(pid, 0);22return true;23} catch {24return false;25}26}2728it('kills blocked extension host on window reload (windowLifecycleBound)', async function () {29this.timeout(60_000);3031const app = this.app as Application;32const pidFile = path.join(app.workspacePathOrFolder, 'vscode-ext-host-pid.txt');3334if (fs.existsSync(pidFile)) {35fs.unlinkSync(pidFile);36}3738await app.workbench.quickaccess.runCommand('smoketest.getExtensionHostPidAndBlock');3940// Wait for PID file to be created41let retries = 0;42while (!fs.existsSync(pidFile) && retries < 20) {43await timeout(500);44retries++;45}4647if (!fs.existsSync(pidFile)) {48throw new Error('PID file was not created - extension may not have activated');49}5051const pid = parseInt(fs.readFileSync(pidFile, 'utf-8'), 10);52logger.log(`Old extension host PID: ${pid}`);5354// Reload window while extension host is blocked55await app.workbench.quickaccess.runCommand('Developer: Reload Window', { keepOpen: true });56await app.code.whenWorkbenchRestored();57logger.log('Window reloaded');5859// Verify old process is gone, allowing for slower teardown on busy machines60const maxWaitMs = 10_000;61const pollIntervalMs = 500;62let waitedMs = 0;63while (processExists(pid) && waitedMs < maxWaitMs) {64await timeout(pollIntervalMs);65waitedMs += pollIntervalMs;66}6768const stillExists = processExists(pid);69if (stillExists) {70throw new Error(`Extension host ${pid} still running after reload (waited ${maxWaitMs}ms)`);71}7273logger.log('Extension host was properly killed on reload');74});7576it('allows extensions to gracefully deactivate on window reload (windowLifecycleGraceTime)', async function () {77this.timeout(60_000);7879const app = this.app as Application;80const pidFile = path.join(app.workspacePathOrFolder, 'vscode-ext-host-pid-graceful.txt');81const markerFile = path.join(app.workspacePathOrFolder, 'vscode-ext-host-deactivated.txt');8283// Clean up any existing files84if (fs.existsSync(pidFile)) {85fs.unlinkSync(pidFile);86}87if (fs.existsSync(markerFile)) {88fs.unlinkSync(markerFile);89}9091// Setup the extension to write a marker file on deactivation92await app.workbench.quickaccess.runCommand('smoketest.setupGracefulDeactivation');9394// Wait for PID file to be created (confirms extension is ready)95let retries = 0;96while (!fs.existsSync(pidFile) && retries < 20) {97await timeout(500);98retries++;99}100101if (!fs.existsSync(pidFile)) {102throw new Error('PID file was not created - extension may not have activated');103}104105const pid = parseInt(fs.readFileSync(pidFile, 'utf-8'), 10);106logger.log(`Extension host PID for graceful deactivation test: ${pid}`);107108// Reload window - this should trigger graceful deactivation109await app.workbench.quickaccess.runCommand('Developer: Reload Window', { keepOpen: true });110await app.code.whenWorkbenchRestored();111logger.log('Window reloaded');112113// Wait for the process to exit and marker file to be written114const maxWaitMs = 10_000;115const pollIntervalMs = 500;116let waitedMs = 0;117while (!fs.existsSync(markerFile) && waitedMs < maxWaitMs) {118await timeout(pollIntervalMs);119waitedMs += pollIntervalMs;120}121122if (!fs.existsSync(markerFile)) {123throw new Error(`Deactivation marker file was not created within ${maxWaitMs}ms - extension may not have been given time to deactivate gracefully`);124}125126logger.log('Extension was given time to gracefully deactivate on reload');127128// Also verify the old process is gone129waitedMs = 0;130while (processExists(pid) && waitedMs < maxWaitMs) {131await timeout(pollIntervalMs);132waitedMs += pollIntervalMs;133}134135if (processExists(pid)) {136throw new Error(`Extension host ${pid} still running after reload (waited ${maxWaitMs}ms)`);137}138139logger.log('Extension host was properly terminated after graceful deactivation');140});141});142}143144145