Skip to content

Commit 9489d85

Browse files
Use a TestAuthUsageService for integration tests and clean up disposables a bit (microsoft#251936)
* Use a `TestAuthUsageService` for integration tests and clean up disposables a bit The test service is needed because otherwise it creates an event that create a disposable... and the test itself doesn't clean that up properly. That service isn't being tested here, so it's replaced with a mocked one. * add a similar class for the mainthread tests
1 parent 763e06f commit 9489d85

File tree

4 files changed

+49
-19
lines changed

4 files changed

+49
-19
lines changed

src/vs/workbench/api/test/browser/extHostAuthentication.integrationTest.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import type { AuthenticationProvider, AuthenticationSession } from 'vscode';
2828
import { IBrowserWorkbenchEnvironmentService } from '../../../services/environment/browser/environmentService.js';
2929
import { IProductService } from '../../../../platform/product/common/productService.js';
3030
import { AuthenticationAccessService, IAuthenticationAccessService } from '../../../services/authentication/browser/authenticationAccessService.js';
31-
import { AuthenticationUsageService, IAuthenticationUsageService } from '../../../services/authentication/browser/authenticationUsageService.js';
31+
import { IAccountUsage, IAuthenticationUsageService } from '../../../services/authentication/browser/authenticationUsageService.js';
3232
import { AuthenticationExtensionsService } from '../../../services/authentication/browser/authenticationExtensionsService.js';
3333
import { ILogService, NullLogService } from '../../../../platform/log/common/log.js';
3434
import { IExtHostInitDataService } from '../../common/extHostInitDataService.js';
@@ -76,6 +76,15 @@ class AuthTestQuickInputService extends TestQuickInputService {
7676
}
7777
}
7878

79+
class TestAuthUsageService implements IAuthenticationUsageService {
80+
_serviceBrand: undefined;
81+
initializeExtensionUsageCache(): Promise<void> { return Promise.resolve(); }
82+
extensionUsesAuth(extensionId: string): Promise<boolean> { return Promise.resolve(false); }
83+
readAccountUsages(providerId: string, accountName: string): IAccountUsage[] { return []; }
84+
removeAccountUsage(providerId: string, accountName: string): void { }
85+
addAccountUsage(providerId: string, accountName: string, scopes: ReadonlyArray<string>, extensionId: string, extensionName: string): void { }
86+
}
87+
7988
class TestAuthProvider implements AuthenticationProvider {
8089
private id = 1;
8190
private sessions = new Map<string, AuthenticationSession>();
@@ -136,7 +145,7 @@ suite('ExtHostAuthentication', () => {
136145
services.set(IUserActivityService, new SyncDescriptor(UserActivityService));
137146
services.set(IAuthenticationAccessService, new SyncDescriptor(AuthenticationAccessService));
138147
services.set(IAuthenticationService, new SyncDescriptor(AuthenticationService));
139-
services.set(IAuthenticationUsageService, new SyncDescriptor(AuthenticationUsageService));
148+
services.set(IAuthenticationUsageService, new SyncDescriptor(TestAuthUsageService));
140149
services.set(IAuthenticationExtensionsService, new SyncDescriptor(AuthenticationExtensionsService));
141150
mainInstantiationService = disposables.add(new TestInstantiationService(services, undefined, undefined, true));
142151

src/vs/workbench/api/test/browser/mainThreadAuthentication.integrationTest.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { TestActivityService, TestExtensionService, TestProductService, TestStor
2626
import { IBrowserWorkbenchEnvironmentService } from '../../../services/environment/browser/environmentService.js';
2727
import { IProductService } from '../../../../platform/product/common/productService.js';
2828
import { AuthenticationAccessService, IAuthenticationAccessService } from '../../../services/authentication/browser/authenticationAccessService.js';
29-
import { AuthenticationUsageService, IAuthenticationUsageService } from '../../../services/authentication/browser/authenticationUsageService.js';
29+
import { IAccountUsage, IAuthenticationUsageService } from '../../../services/authentication/browser/authenticationUsageService.js';
3030
import { AuthenticationExtensionsService } from '../../../services/authentication/browser/authenticationExtensionsService.js';
3131
import { ILogService, NullLogService } from '../../../../platform/log/common/log.js';
3232
import { IHostService } from '../../../services/host/browser/host.js';
@@ -40,6 +40,15 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/c
4040
import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';
4141
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
4242

43+
class TestAuthUsageService implements IAuthenticationUsageService {
44+
_serviceBrand: undefined;
45+
initializeExtensionUsageCache(): Promise<void> { return Promise.resolve(); }
46+
extensionUsesAuth(extensionId: string): Promise<boolean> { return Promise.resolve(false); }
47+
readAccountUsages(providerId: string, accountName: string): IAccountUsage[] { return []; }
48+
removeAccountUsage(providerId: string, accountName: string): void { }
49+
addAccountUsage(providerId: string, accountName: string, scopes: ReadonlyArray<string>, extensionId: string, extensionName: string): void { }
50+
}
51+
4352
suite('MainThreadAuthentication', () => {
4453
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
4554

@@ -64,7 +73,7 @@ suite('MainThreadAuthentication', () => {
6473
services.set(IUserActivityService, new SyncDescriptor(UserActivityService));
6574
services.set(IAuthenticationAccessService, new SyncDescriptor(AuthenticationAccessService));
6675
services.set(IAuthenticationService, new SyncDescriptor(AuthenticationService));
67-
services.set(IAuthenticationUsageService, new SyncDescriptor(AuthenticationUsageService));
76+
services.set(IAuthenticationUsageService, new SyncDescriptor(TestAuthUsageService));
6877
services.set(IAuthenticationExtensionsService, new SyncDescriptor(AuthenticationExtensionsService));
6978
instantiationService = disposables.add(new TestInstantiationService(services, undefined, undefined, true));
7079

src/vs/workbench/services/authentication/browser/authenticationService.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import { ExtensionsRegistry } from '../../extensions/common/extensionsRegistry.j
2121
import { match } from '../../../../base/common/glob.js';
2222
import { URI } from '../../../../base/common/uri.js';
2323
import { IAuthorizationProtectedResourceMetadata, IAuthorizationServerMetadata } from '../../../../base/common/oauth.js';
24-
import { raceTimeout } from '../../../../base/common/async.js';
24+
import { raceCancellation, raceTimeout } from '../../../../base/common/async.js';
25+
import { CancellationTokenSource } from '../../../../base/common/cancellation.js';
2526

2627
export function getAuthenticationProviderActivationEvent(id: string): string { return `onAuthenticationRequest:${id}`; }
2728

@@ -110,7 +111,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
110111

111112
private readonly _delegates: IAuthenticationProviderHostDelegate[] = [];
112113

113-
private _isDisposable: boolean = false;
114+
private _disposedSource = new CancellationTokenSource();
114115

115116
constructor(
116117
@IExtensionService private readonly _extensionService: IExtensionService,
@@ -119,7 +120,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
119120
@ILogService private readonly _logService: ILogService
120121
) {
121122
super();
122-
this._register(toDisposable(() => this._isDisposable = true));
123+
this._register(toDisposable(() => this._disposedSource.dispose(true)));
123124
this._register(authenticationAccessService.onDidChangeExtensionSessionAccess(e => {
124125
// The access has changed, not the actual session itself but extensions depend on this event firing
125126
// when they have gained access to an account so this fires that event.
@@ -276,7 +277,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
276277
}
277278

278279
async getSessions(id: string, scopes?: string[], options?: IAuthenticationGetSessionsOptions, activateImmediate: boolean = false): Promise<ReadonlyArray<AuthenticationSession>> {
279-
if (this._isDisposable) {
280+
if (this._disposedSource.token.isCancellationRequested) {
280281
return [];
281282
}
282283

@@ -297,7 +298,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
297298
}
298299

299300
async createSession(id: string, scopes: string[], options?: IAuthenticationCreateSessionOptions): Promise<AuthenticationSession> {
300-
if (this._isDisposable) {
301+
if (this._disposedSource.token.isCancellationRequested) {
301302
throw new Error('Authentication service is disposed.');
302303
}
303304

@@ -310,7 +311,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
310311
}
311312

312313
async removeSession(id: string, sessionId: string): Promise<void> {
313-
if (this._isDisposable) {
314+
if (this._disposedSource.token.isCancellationRequested) {
314315
throw new Error('Authentication service is disposed.');
315316
}
316317

@@ -382,17 +383,23 @@ export class AuthenticationService extends Disposable implements IAuthentication
382383
if (provider) {
383384
return provider;
384385
}
386+
if (this._disposedSource.token.isCancellationRequested) {
387+
throw new Error('Authentication service is disposed.');
388+
}
385389

386-
const store = this._register(new DisposableStore());
390+
const store = new DisposableStore();
387391
try {
388392
const result = await raceTimeout(
389-
Event.toPromise(
390-
Event.filter(
391-
this.onDidRegisterAuthenticationProvider,
392-
e => e.id === providerId,
393+
raceCancellation(
394+
Event.toPromise(
395+
Event.filter(
396+
this.onDidRegisterAuthenticationProvider,
397+
e => e.id === providerId,
398+
store
399+
),
393400
store
394401
),
395-
store
402+
this._disposedSource.token
396403
),
397404
5000
398405
);

src/vs/workbench/services/authentication/browser/authenticationUsageService.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Queue } from '../../../../base/common/async.js';
7-
import { Disposable } from '../../../../base/common/lifecycle.js';
7+
import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js';
88
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
99
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
1010
import { ILogService } from '../../../../platform/log/common/log.js';
@@ -56,17 +56,19 @@ export interface IAuthenticationUsageService {
5656
export class AuthenticationUsageService extends Disposable implements IAuthenticationUsageService {
5757
_serviceBrand: undefined;
5858

59-
private _queue = new Queue();
59+
private _queue = this._register(new Queue());
6060
private _extensionsUsingAuth = new Set<string>();
6161

62+
private _disposed = false;
63+
6264
constructor(
6365
@IStorageService private readonly _storageService: IStorageService,
6466
@IAuthenticationService private readonly _authenticationService: IAuthenticationService,
6567
@ILogService private readonly _logService: ILogService,
6668
@IProductService productService: IProductService,
6769
) {
6870
super();
69-
71+
this._register(toDisposable(() => this._disposed = true));
7072
// If an extension is listed in `trustedExtensionAuthAccess` we should consider it as using auth
7173
const trustedExtensionAuthAccess = productService.trustedExtensionAuthAccess;
7274
if (Array.isArray(trustedExtensionAuthAccess)) {
@@ -143,6 +145,9 @@ export class AuthenticationUsageService extends Disposable implements IAuthentic
143145
}
144146

145147
private async _addExtensionsToCache(providerId: string) {
148+
if (this._disposed) {
149+
return;
150+
}
146151
try {
147152
const accounts = await this._authenticationService.getAccounts(providerId);
148153
for (const account of accounts) {

0 commit comments

Comments
 (0)