Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/test/smoke/src/areas/extensions/extension-host-restart.test.ts
5344 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as fs from 'fs';
7
import * as path from 'path';
8
import { Application, Logger } from '../../../../automation';
9
import { installAllHandlers, timeout } from '../../utils';
10
11
/**
12
* Verifies that window reload kills the extension host even when blocked.
13
*
14
*/
15
export function setup(logger: Logger) {
16
describe('Extension Host Restart', () => {
17
18
installAllHandlers(logger, opts => opts);
19
20
function processExists(pid: number): boolean {
21
try {
22
process.kill(pid, 0);
23
return true;
24
} catch {
25
return false;
26
}
27
}
28
29
it('kills blocked extension host on window reload (windowLifecycleBound)', async function () {
30
this.timeout(60_000);
31
32
const app = this.app as Application;
33
const pidFile = path.join(app.workspacePathOrFolder, 'vscode-ext-host-pid.txt');
34
35
if (fs.existsSync(pidFile)) {
36
fs.unlinkSync(pidFile);
37
}
38
39
await app.workbench.quickaccess.runCommand('smoketest.getExtensionHostPidAndBlock');
40
41
// Wait for PID file to be created
42
let retries = 0;
43
while (!fs.existsSync(pidFile) && retries < 20) {
44
await timeout(500);
45
retries++;
46
}
47
48
if (!fs.existsSync(pidFile)) {
49
throw new Error('PID file was not created - extension may not have activated');
50
}
51
52
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8'), 10);
53
logger.log(`Old extension host PID: ${pid}`);
54
55
// Reload window while extension host is blocked
56
await app.workbench.quickaccess.runCommand('Developer: Reload Window', { keepOpen: true });
57
await app.code.whenWorkbenchRestored();
58
logger.log('Window reloaded');
59
60
// Verify old process is gone, allowing for slower teardown on busy machines
61
const maxWaitMs = 10_000;
62
const pollIntervalMs = 500;
63
let waitedMs = 0;
64
while (processExists(pid) && waitedMs < maxWaitMs) {
65
await timeout(pollIntervalMs);
66
waitedMs += pollIntervalMs;
67
}
68
69
const stillExists = processExists(pid);
70
if (stillExists) {
71
throw new Error(`Extension host ${pid} still running after reload (waited ${maxWaitMs}ms)`);
72
}
73
74
logger.log('Extension host was properly killed on reload');
75
});
76
77
it('allows extensions to gracefully deactivate on window reload (windowLifecycleGraceTime)', async function () {
78
this.timeout(60_000);
79
80
const app = this.app as Application;
81
const pidFile = path.join(app.workspacePathOrFolder, 'vscode-ext-host-pid-graceful.txt');
82
const markerFile = path.join(app.workspacePathOrFolder, 'vscode-ext-host-deactivated.txt');
83
84
// Clean up any existing files
85
if (fs.existsSync(pidFile)) {
86
fs.unlinkSync(pidFile);
87
}
88
if (fs.existsSync(markerFile)) {
89
fs.unlinkSync(markerFile);
90
}
91
92
// Setup the extension to write a marker file on deactivation
93
await app.workbench.quickaccess.runCommand('smoketest.setupGracefulDeactivation');
94
95
// Wait for PID file to be created (confirms extension is ready)
96
let retries = 0;
97
while (!fs.existsSync(pidFile) && retries < 20) {
98
await timeout(500);
99
retries++;
100
}
101
102
if (!fs.existsSync(pidFile)) {
103
throw new Error('PID file was not created - extension may not have activated');
104
}
105
106
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8'), 10);
107
logger.log(`Extension host PID for graceful deactivation test: ${pid}`);
108
109
// Reload window - this should trigger graceful deactivation
110
await app.workbench.quickaccess.runCommand('Developer: Reload Window', { keepOpen: true });
111
await app.code.whenWorkbenchRestored();
112
logger.log('Window reloaded');
113
114
// Wait for the process to exit and marker file to be written
115
const maxWaitMs = 10_000;
116
const pollIntervalMs = 500;
117
let waitedMs = 0;
118
while (!fs.existsSync(markerFile) && waitedMs < maxWaitMs) {
119
await timeout(pollIntervalMs);
120
waitedMs += pollIntervalMs;
121
}
122
123
if (!fs.existsSync(markerFile)) {
124
throw new Error(`Deactivation marker file was not created within ${maxWaitMs}ms - extension may not have been given time to deactivate gracefully`);
125
}
126
127
logger.log('Extension was given time to gracefully deactivate on reload');
128
129
// Also verify the old process is gone
130
waitedMs = 0;
131
while (processExists(pid) && waitedMs < maxWaitMs) {
132
await timeout(pollIntervalMs);
133
waitedMs += pollIntervalMs;
134
}
135
136
if (processExists(pid)) {
137
throw new Error(`Extension host ${pid} still running after reload (waited ${maxWaitMs}ms)`);
138
}
139
140
logger.log('Extension host was properly terminated after graceful deactivation');
141
});
142
});
143
}
144
145