Skip to content

Commit 9d0c0b7

Browse files
authored
Smoke test driver.exitApplication sometimes does not work (microsoft#157979) (microsoft#158479)
1 parent 0326620 commit 9d0c0b7

File tree

4 files changed

+35
-45
lines changed

4 files changed

+35
-45
lines changed

src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ILogService } from 'vs/platform/log/common/log';
1717
import { IStateMainService } from 'vs/platform/state/electron-main/state';
1818
import { ICodeWindow, LoadReason, UnloadReason } from 'vs/platform/window/electron-main/window';
1919
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
20+
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
2021

2122
export const ILifecycleMainService = createDecorator<ILifecycleMainService>('lifecycleMainService');
2223

@@ -219,7 +220,8 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
219220

220221
constructor(
221222
@ILogService private readonly logService: ILogService,
222-
@IStateMainService private readonly stateMainService: IStateMainService
223+
@IStateMainService private readonly stateMainService: IStateMainService,
224+
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService
223225
) {
224226
super();
225227

@@ -245,11 +247,11 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
245247
return;
246248
}
247249

248-
this.logService.trace('Lifecycle#app.on(before-quit)');
250+
this.trace('Lifecycle#app.on(before-quit)');
249251
this._quitRequested = true;
250252

251253
// Emit event to indicate that we are about to shutdown
252-
this.logService.trace('Lifecycle#onBeforeShutdown.fire()');
254+
this.trace('Lifecycle#onBeforeShutdown.fire()');
253255
this._onBeforeShutdown.fire();
254256

255257
// macOS: can run without any window open. in that case we fire
@@ -265,7 +267,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
265267
// was closed. We override this event to be in charge if app.quit()
266268
// should be called or not.
267269
const windowAllClosedListener = () => {
268-
this.logService.trace('Lifecycle#app.on(window-all-closed)');
270+
this.trace('Lifecycle#app.on(window-all-closed)');
269271

270272
// Windows/Linux: we quit when all windows have closed
271273
// Mac: we only quit when quit was requested
@@ -278,7 +280,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
278280
// will-quit: an event that is fired after all windows have been
279281
// closed, but before actually quitting.
280282
app.once('will-quit', e => {
281-
this.logService.trace('Lifecycle#app.on(will-quit)');
283+
this.trace('Lifecycle#app.on(will-quit)');
282284

283285
// Prevent the quit until the shutdown promise was resolved
284286
e.preventDefault();
@@ -307,7 +309,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
307309
return this.pendingWillShutdownPromise; // shutdown is already running
308310
}
309311

310-
this.logService.trace('Lifecycle#onWillShutdown.fire()');
312+
this.trace('Lifecycle#onWillShutdown.fire()');
311313

312314
const joiners: Promise<void>[] = [];
313315

@@ -348,7 +350,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
348350
return;
349351
}
350352

351-
this.logService.trace(`lifecycle (main): phase changed (value: ${value})`);
353+
this.trace(`lifecycle (main): phase changed (value: ${value})`);
352354

353355
this._phase = value;
354356

@@ -394,7 +396,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
394396
return;
395397
}
396398

397-
this.logService.trace(`Lifecycle#window.on('close') - window ID ${window.id}`);
399+
this.trace(`Lifecycle#window.on('close') - window ID ${window.id}`);
398400

399401
// Otherwise prevent unload and handle it from window
400402
e.preventDefault();
@@ -407,7 +409,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
407409
this.windowToCloseRequest.add(windowId);
408410

409411
// Fire onBeforeCloseWindow before actually closing
410-
this.logService.trace(`Lifecycle#onBeforeCloseWindow.fire() - window ID ${windowId}`);
412+
this.trace(`Lifecycle#onBeforeCloseWindow.fire() - window ID ${windowId}`);
411413
this._onBeforeCloseWindow.fire(window);
412414

413415
// No veto, close window now
@@ -417,7 +419,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
417419

418420
// Window After Closing
419421
win.on('closed', () => {
420-
this.logService.trace(`Lifecycle#window.on('closed') - window ID ${window.id}`);
422+
this.trace(`Lifecycle#window.on('closed') - window ID ${window.id}`);
421423

422424
// update window count
423425
this.windowCounter--;
@@ -467,13 +469,13 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
467469
return false;
468470
}
469471

470-
this.logService.trace(`Lifecycle#unload() - window ID ${window.id}`);
472+
this.trace(`Lifecycle#unload() - window ID ${window.id}`);
471473

472474
// first ask the window itself if it vetos the unload
473475
const windowUnloadReason = this._quitRequested ? UnloadReason.QUIT : reason;
474476
const veto = await this.onBeforeUnloadWindowInRenderer(window, windowUnloadReason);
475477
if (veto) {
476-
this.logService.trace(`Lifecycle#unload() - veto in renderer (window ID ${window.id})`);
478+
this.trace(`Lifecycle#unload() - veto in renderer (window ID ${window.id})`);
477479

478480
return this.handleWindowUnloadVeto(veto);
479481
}
@@ -536,12 +538,14 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
536538
}
537539

538540
quit(willRestart?: boolean): Promise<boolean /* veto */> {
541+
this.trace(`Lifecycle#quit() - begin (willRestart: ${willRestart})`);
542+
539543
if (this.pendingQuitPromise) {
544+
this.trace('Lifecycle#quit() - returning pending quit promise');
545+
540546
return this.pendingQuitPromise;
541547
}
542548

543-
this.logService.trace(`Lifecycle#quit() - will restart: ${willRestart}`);
544-
545549
// Remember if we are about to restart
546550
if (willRestart) {
547551
this.stateMainService.setItem(LifecycleMainService.QUIT_AND_RESTART_KEY, true);
@@ -554,15 +558,23 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
554558

555559
// Calling app.quit() will trigger the close handlers of each opened window
556560
// and only if no window vetoed the shutdown, we will get the will-quit event
557-
this.logService.trace('Lifecycle#quit() - calling app.quit()');
561+
this.trace('Lifecycle#quit() - calling app.quit()');
558562
app.quit();
559563
});
560564

561565
return this.pendingQuitPromise;
562566
}
563567

568+
private trace(msg: string): void {
569+
if (this.environmentMainService.args['enable-smoke-test-driver']) {
570+
this.logService.info(msg); // helps diagnose issues with exiting from smoke tests
571+
} else {
572+
this.logService.trace(msg);
573+
}
574+
}
575+
564576
async relaunch(options?: { addArgs?: string[]; removeArgs?: string[] }): Promise<void> {
565-
this.logService.trace('Lifecycle#relaunch()');
577+
this.trace('Lifecycle#relaunch()');
566578

567579
const args = process.argv.slice(1);
568580
if (options?.addArgs) {
@@ -595,7 +607,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
595607
}
596608

597609
// relaunch after we are sure there is no veto
598-
this.logService.trace('Lifecycle#relaunch() - calling app.relaunch()');
610+
this.trace('Lifecycle#relaunch() - calling app.relaunch()');
599611
app.relaunch({ args });
600612
};
601613
app.once('quit', quitListener);
@@ -609,7 +621,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
609621
}
610622

611623
async kill(code?: number): Promise<void> {
612-
this.logService.trace('Lifecycle#kill()');
624+
this.trace('Lifecycle#kill()');
613625

614626
// Give main process participants a chance to orderly shutdown
615627
await this.fireOnWillShutdown(ShutdownReason.KILL);

src/vs/workbench/electron-sandbox/window.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,8 @@ export class NativeWindow extends Disposable {
660660
const that = this;
661661
registerWindowDriver({
662662
async exitApplication(): Promise<void> {
663+
that.logService.info('[driver] handling exitApplication()');
664+
663665
return that.nativeHostService.quit();
664666
}
665667
});

test/automation/src/code.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export class Code {
140140
}
141141

142142
async exit(): Promise<void> {
143-
return measureAndLog(new Promise<void>((resolve, reject) => {
143+
return measureAndLog(new Promise<void>(resolve => {
144144
const pid = this.mainProcess.pid!;
145145

146146
let done = false;
@@ -154,8 +154,8 @@ export class Code {
154154
while (!done) {
155155
retries++;
156156

157-
if (retries === 20) {
158-
this.logger.log('Smoke test exit call did not terminate process after 10s, forcefully exiting the application...');
157+
if (retries === 40) {
158+
this.logger.log('Smoke test exit call did not terminate process after 20s, forcefully exiting the application...');
159159

160160
// no need to await since we're polling for the process to die anyways
161161
treekill(pid, err => {

test/smoke/src/areas/workbench/data-loss.test.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,6 @@ import { createApp, timeout, installDiagnosticsHandler, installAppAfterHandler,
99

1010
export function setup(ensureStableCode: () => string | undefined, logger: Logger) {
1111
describe('Data Loss (insiders -> insiders)', function () {
12-
13-
// There are cases where `exitApplication` does not actually
14-
// stop the application and our attempt then to `kill` the
15-
// process tree results in data loss / state loss. All these
16-
// tests here rely on state getting persisted properly, so
17-
// until we have figured out the root cause, we retry these
18-
// tests.
19-
// See: https://github.com/microsoft/vscode/issues/157979
20-
if (process.platform === 'darwin') {
21-
this.retries(2);
22-
}
23-
2412
let app: Application | undefined = undefined;
2513

2614
// Shared before/after handling
@@ -142,18 +130,6 @@ export function setup(ensureStableCode: () => string | undefined, logger: Logger
142130
});
143131

144132
describe('Data Loss (stable -> insiders)', function () {
145-
146-
// There are cases where `exitApplication` does not actually
147-
// stop the application and our attempt then to `kill` the
148-
// process tree results in data loss / state loss. All these
149-
// tests here rely on state getting persisted properly, so
150-
// until we have figured out the root cause, we retry these
151-
// tests.
152-
// See: https://github.com/microsoft/vscode/issues/157979
153-
if (process.platform === 'darwin') {
154-
this.retries(2);
155-
}
156-
157133
let insidersApp: Application | undefined = undefined;
158134
let stableApp: Application | undefined = undefined;
159135

0 commit comments

Comments
 (0)