From 8adddf5d4839b1c7af016994cb0ad0803c7bfa99 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 7 Sep 2023 09:30:19 +0200 Subject: [PATCH] debt - dispose things in my unit tests (#192112) * debt - dispose in `WorkingCopyHistoryTracker` * adopt more * more * more * more * fix * fix * input * input * input * input * input * input * input * input * wtf * . * . * inp * . * fix compile error * fux * give up * fixup disposable * testing: fix cannot read properties of undefined (reading 'layout') (#192340) Fixes #189326 * input --------- Co-authored-by: Connor Peet --- src/vs/base/common/stream.ts | 4 + src/vs/editor/common/model.ts | 2 +- src/vs/platform/files/common/fileService.ts | 5 +- .../storage/electron-main/storageMain.ts | 4 +- .../electron-main/storageMainService.ts | 18 +-- .../test/common/storageService.test.ts | 17 ++- .../electron-main/storageMainService.test.ts | 66 ++++---- .../browser/parts/editor/editorAutoSave.ts | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 4 +- .../extensionsActions.test.ts | 5 +- .../files/test/browser/editorAutoSave.test.ts | 32 ++-- .../test/browser/fileEditorInput.test.ts | 5 +- .../browser/textFileEditorTracker.test.ts | 47 +++--- .../test/browser/testNotebookEditor.ts | 5 +- .../testing/browser/testingExplorerView.ts | 2 +- .../editor/test/browser/editorService.test.ts | 17 +-- .../extensionEnablementService.test.ts | 8 +- ...extensionManifestPropertiesService.test.ts | 3 +- .../test/browser/historyService.test.ts | 2 +- .../electron-sandbox/lifecycleService.test.ts | 6 +- .../test/browser/storageService.test.ts | 41 +++-- .../browser/browserTextFileService.io.test.ts | 16 +- .../test/browser/textFileEditorModel.test.ts | 7 +- .../textFileEditorModelManager.test.ts | 5 +- .../test/browser/textFileService.test.ts | 7 +- .../test/common/textFileService.io.test.ts | 32 ++-- .../nativeTextFileService.io.test.ts | 13 +- .../nativeTextFileService.test.ts | 19 ++- .../browser/textModelResolverService.test.ts | 9 +- .../test/browser/untitledTextEditor.test.ts | 7 +- .../common/workingCopyBackupService.ts | 12 +- .../common/workingCopyBackupTracker.ts | 63 +++++--- .../common/workingCopyHistoryService.ts | 2 +- .../workingCopyBackupService.ts | 2 +- .../browser/fileWorkingCopyManager.test.ts | 10 +- .../test/browser/resourceWorkingCopy.test.ts | 8 +- .../browser/storedFileWorkingCopy.test.ts | 16 +- .../storedFileWorkingCopyManager.test.ts | 10 +- .../browser/untitledFileWorkingCopy.test.ts | 8 +- .../untitledFileWorkingCopyManager.test.ts | 10 +- .../untitledScratchpadWorkingCopy.test.ts | 8 +- .../browser/workingCopyBackupTracker.test.ts | 77 ++++------ .../browser/workingCopyEditorService.test.ts | 26 ++-- .../browser/workingCopyFileService.test.ts | 7 +- .../workingCopyBackupService.test.ts | 12 +- .../workingCopyBackupTracker.test.ts | 47 +++--- .../workingCopyHistoryService.test.ts | 114 +++++++------- .../workingCopyHistoryTracker.test.ts | 99 ++++-------- .../test/common/testWorkspaceTrustService.ts | 144 ------------------ .../test/common/workspaceTrust.test.ts | 3 +- .../parts/editor/diffEditorInput.test.ts | 8 +- .../test/browser/parts/editor/editor.test.ts | 45 +++--- .../parts/editor/editorDiffModel.test.ts | 5 +- .../browser/parts/editor/editorInput.test.ts | 5 +- .../browser/parts/editor/editorPane.test.ts | 96 ++++++------ .../parts/editor/resourceEditorInput.test.ts | 5 +- .../editor/sideBySideEditorInput.test.ts | 8 +- .../parts/editor/textEditorPane.test.ts | 15 +- .../editor/textResourceEditorInput.test.ts | 6 +- .../test/browser/workbenchTestServices.ts | 64 +++++--- .../test/common/workbenchTestServices.ts | 139 +++++++++++++++++ .../electron-sandbox/workbenchTestServices.ts | 18 +-- 62 files changed, 755 insertions(+), 747 deletions(-) delete mode 100644 src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 055558fc748..d6cd674b1de 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -622,11 +622,15 @@ export function peekStream(stream: ReadableStream, maxChunks: number): Pro // Error Listener const errorListener = (error: Error) => { + streamListeners.dispose(); + return reject(error); }; // End Listener const endListener = () => { + streamListeners.dispose(); + return resolve({ stream, buffer, ended: true }); }; diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index ea74d5c19b9..3d9f01f7dfa 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1395,7 +1395,7 @@ export class SearchData { /** * @internal */ -export interface ITextBuffer extends IReadonlyTextBuffer { +export interface ITextBuffer extends IReadonlyTextBuffer, IDisposable { setEOL(newEOL: '\r\n' | '\n'): void; applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult; } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index e88ff723b8b..b65c4e8705a 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -537,7 +537,7 @@ export class FileService extends Disposable implements IFileService { // validate read operation const statPromise = this.validateReadFile(resource, options).then(stat => stat, error => { - cancellableSource.cancel(); + cancellableSource.dispose(true); throw error; }); @@ -572,6 +572,9 @@ export class FileService extends Disposable implements IFileService { fileStream = this.readFileBuffered(provider, resource, cancellableSource.token, options); } + fileStream.on('end', () => cancellableSource.dispose()); + fileStream.on('error', () => cancellableSource.dispose()); + const fileStat = await statPromise; return { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 6843793692f..c110f28015b 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -122,7 +122,7 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { private readonly _onDidCloseStorage = this._register(new Emitter()); readonly onDidCloseStorage = this._onDidCloseStorage.event; - private _storage = new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY }); // storage is in-memory until initialized + private _storage = this._register(new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY })); // storage is in-memory until initialized get storage(): IStorage { return this._storage; } abstract get path(): string | undefined; @@ -155,7 +155,7 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { try { // Create storage via subclasses - const storage = await this.doCreate(); + const storage = this._register(await this.doCreate()); // Replace our in-memory storage with the real // once as soon as possible without awaiting diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index bdfad4eacb6..4d5c2eb209a 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -164,16 +164,16 @@ export class StorageMainService extends Disposable implements IStorageMainServic //#region Application Storage - readonly applicationStorage = this.createApplicationStorage(); + readonly applicationStorage = this._register(this.createApplicationStorage()); private createApplicationStorage(): IStorageMain { this.logService.trace(`StorageMainService: creating application storage`); const applicationStorage = new ApplicationStorageMain(this.getStorageOptions(), this.userDataProfilesService, this.logService, this.fileService); - once(applicationStorage.onDidCloseStorage)(() => { + this._register(once(applicationStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed application storage`); - }); + })); return applicationStorage; } @@ -193,7 +193,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic if (!profileStorage) { this.logService.trace(`StorageMainService: creating profile storage (${profile.name})`); - profileStorage = this.createProfileStorage(profile); + profileStorage = this._register(this.createProfileStorage(profile)); this.mapProfileToStorage.set(profile.id, profileStorage); const listener = this._register(profileStorage.onDidChangeStorage(e => this._onDidChangeProfileStorage.fire({ @@ -202,12 +202,12 @@ export class StorageMainService extends Disposable implements IStorageMainServic profile }))); - once(profileStorage.onDidCloseStorage)(() => { + this._register(once(profileStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed profile storage (${profile.name})`); this.mapProfileToStorage.delete(profile.id); listener.dispose(); - }); + })); } return profileStorage; @@ -238,14 +238,14 @@ export class StorageMainService extends Disposable implements IStorageMainServic if (!workspaceStorage) { this.logService.trace(`StorageMainService: creating workspace storage (${workspace.id})`); - workspaceStorage = this.createWorkspaceStorage(workspace); + workspaceStorage = this._register(this.createWorkspaceStorage(workspace)); this.mapWorkspaceToStorage.set(workspace.id, workspaceStorage); - once(workspaceStorage.onDidCloseStorage)(() => { + this._register(once(workspaceStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed workspace storage (${workspace.id})`); this.mapWorkspaceToStorage.delete(workspace.id); - }); + })); } return workspaceStorage; diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index 4ace3be8706..93ac0d93721 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -11,11 +11,14 @@ export function createSuite(params: { setup: () => Pr let storageService: T; + const disposables = new DisposableStore(); + setup(async () => { storageService = await params.setup(); }); teardown(() => { + disposables.clear(); return params.teardown(storageService); }); @@ -33,7 +36,7 @@ export function createSuite(params: { setup: () => Pr test('Storage change source', () => { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); // Explicit external source storageService.storeAll([{ key: 'testExternalChange', value: 'foobar', scope: StorageScope.WORKSPACE, target: StorageTarget.MACHINE }], true); @@ -52,7 +55,7 @@ export function createSuite(params: { setup: () => Pr test('Storage change event scope (all keys)', () => { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); storageService.store('testChange', 'foobar', StorageScope.WORKSPACE, StorageTarget.MACHINE); storageService.store('testChange2', 'foobar', StorageScope.WORKSPACE, StorageTarget.MACHINE); @@ -64,7 +67,7 @@ export function createSuite(params: { setup: () => Pr test('Storage change event scope (specific key)', () => { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(StorageScope.WORKSPACE, 'testChange', new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(StorageScope.WORKSPACE, 'testChange', disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); storageService.store('testChange', 'foobar', StorageScope.WORKSPACE, StorageTarget.MACHINE); storageService.store('testChange', 'foobar', StorageScope.PROFILE, StorageTarget.USER); @@ -77,7 +80,7 @@ export function createSuite(params: { setup: () => Pr function storeData(scope: StorageScope): void { let storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(scope, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(scope, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); strictEqual(storageService.get('test.get', scope, 'foobar'), 'foobar'); strictEqual(storageService.get('test.get', scope, ''), ''); @@ -153,7 +156,7 @@ export function createSuite(params: { setup: () => Pr function removeData(scope: StorageScope): void { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(scope, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(scope, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); storageService.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); strictEqual('foobar', storageService.get('test.remove', scope, (undefined)!)); @@ -167,7 +170,7 @@ export function createSuite(params: { setup: () => Pr test('Keys (in-memory)', () => { let storageTargetEvent: IStorageTargetChangeEvent | undefined = undefined; - storageService.onDidChangeTarget(e => storageTargetEvent = e); + storageService.onDidChangeTarget(e => storageTargetEvent = e, undefined, disposables); // Empty for (const scope of [StorageScope.WORKSPACE, StorageScope.PROFILE, StorageScope.APPLICATION]) { @@ -180,7 +183,7 @@ export function createSuite(params: { setup: () => Pr // Add values for (const scope of [StorageScope.WORKSPACE, StorageScope.PROFILE, StorageScope.APPLICATION]) { - storageService.onDidChangeValue(scope, undefined, new DisposableStore())(e => storageValueChangeEvent = e); + storageService.onDidChangeValue(scope, undefined, disposables)(e => storageValueChangeEvent = e, undefined, disposables); for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { storageTargetEvent = Object.create(null); diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 3ca4ea12ffb..f3467af580f 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -24,9 +24,13 @@ import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentitySe import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; import { TestLifecycleMainService } from 'vs/platform/test/electron-main/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('StorageMainService', function () { + const disposables = new DisposableStore(); + const productService: IProductService = { _serviceBrand: undefined, ...product }; const inMemoryProfileRoot = URI.file('/location').with({ scheme: Schemas.inMemory }); @@ -68,12 +72,12 @@ suite('StorageMainService', function () { } let storageChangeEvent: IStorageChangeEvent | undefined = undefined; - const storageChangeListener = storage.onDidChangeStorage(e => { + disposables.add(storage.onDidChangeStorage(e => { storageChangeEvent = e; - }); + })); let storageDidClose = false; - const storageCloseListener = storage.onDidCloseStorage(() => storageDidClose = true); + disposables.add(storage.onDidCloseStorage(() => storageDidClose = true)); // Basic store/get/remove const size = storage.items.size; @@ -101,15 +105,21 @@ suite('StorageMainService', function () { await storage.close(); strictEqual(storageDidClose, true); - - storageChangeListener.dispose(); - storageCloseListener.dispose(); } + teardown(() => { + disposables.clear(); + }); + function createStorageService(lifecycleMainService: ILifecycleMainService = new TestLifecycleMainService()): TestStorageMainService { const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); - const fileService = new FileService(new NullLogService()); - return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesMainService(new StateService(SaveStrategy.DELAYED, environmentService, new NullLogService(), fileService), new UriIdentityService(fileService), environmentService, fileService, new NullLogService()), lifecycleMainService, fileService, new UriIdentityService(fileService)); + const fileService = disposables.add(new FileService(new NullLogService())); + const uriIdentityService = disposables.add(new UriIdentityService(fileService)); + const testStorageService = disposables.add(new TestStorageMainService(new NullLogService(), environmentService, disposables.add(new UserDataProfilesMainService(new StateService(SaveStrategy.DELAYED, environmentService, new NullLogService(), fileService), disposables.add(uriIdentityService), environmentService, fileService, new NullLogService())), lifecycleMainService, fileService, uriIdentityService)); + + disposables.add(testStorageService.applicationStorage); + + return testStorageService; } test('basics (application)', function () { @@ -141,21 +151,21 @@ suite('StorageMainService', function () { const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; - workspaceStorage.onDidCloseStorage(() => { + disposables.add(workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; - }); + })); const profileStorage = storageMainService.profileStorage(profile); let didCloseProfileStorage = false; - profileStorage.onDidCloseStorage(() => { + disposables.add(profileStorage.onDidCloseStorage(() => { didCloseProfileStorage = true; - }); + })); const applicationStorage = storageMainService.applicationStorage; let didCloseApplicationStorage = false; - applicationStorage.onDidCloseStorage(() => { + disposables.add(applicationStorage.onDidCloseStorage(() => { didCloseApplicationStorage = true; - }); + })); strictEqual(applicationStorage, storageMainService.applicationStorage); // same instance as long as not closed strictEqual(profileStorage, storageMainService.profileStorage(profile)); // same instance as long as not closed @@ -177,7 +187,7 @@ suite('StorageMainService', function () { const workspaceStorage2 = storageMainService.workspaceStorage(workspace); notStrictEqual(workspaceStorage, workspaceStorage2); - return workspaceStorage2.close(); + await workspaceStorage2.close(); }); test('storage closed before init works', async function () { @@ -187,21 +197,21 @@ suite('StorageMainService', function () { const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; - workspaceStorage.onDidCloseStorage(() => { + disposables.add(workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; - }); + })); const profileStorage = storageMainService.profileStorage(profile); let didCloseProfileStorage = false; - profileStorage.onDidCloseStorage(() => { + disposables.add(profileStorage.onDidCloseStorage(() => { didCloseProfileStorage = true; - }); + })); const applicationStorage = storageMainService.applicationStorage; let didCloseApplicationStorage = false; - applicationStorage.onDidCloseStorage(() => { + disposables.add(applicationStorage.onDidCloseStorage(() => { didCloseApplicationStorage = true; - }); + })); await applicationStorage.close(); await profileStorage.close(); @@ -219,21 +229,21 @@ suite('StorageMainService', function () { const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; - workspaceStorage.onDidCloseStorage(() => { + disposables.add(workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; - }); + })); const profileStorage = storageMainService.profileStorage(profile); let didCloseProfileStorage = false; - profileStorage.onDidCloseStorage(() => { + disposables.add(profileStorage.onDidCloseStorage(() => { didCloseProfileStorage = true; - }); + })); const applicationtorage = storageMainService.applicationStorage; let didCloseApplicationStorage = false; - applicationtorage.onDidCloseStorage(() => { + disposables.add(applicationtorage.onDidCloseStorage(() => { didCloseApplicationStorage = true; - }); + })); applicationtorage.init(); profileStorage.init(); @@ -247,4 +257,6 @@ suite('StorageMainService', function () { strictEqual(didCloseProfileStorage, true); strictEqual(didCloseWorkspaceStorage, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index 1de67c853f8..5fed6253090 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -191,7 +191,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution const handle = setTimeout(() => { // Clear disposable - this.pendingAutoSavesAfterDelay.delete(workingCopy); + this.discardAutoSave(workingCopy); // Save if dirty if (workingCopy.isDirty()) { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 5d6b14d633a..ac3fced8d68 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -808,13 +808,13 @@ export class TabsTitleControl extends TitleControl { const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index }); const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner }); - tabActionBar.onWillRun(e => { + const tabActionListener = tabActionBar.onWillRun(e => { if (e.action.id === this.closeEditorAction.id) { this.blockRevealActiveTabOnce(); } }); - const tabActionBarDisposable = combinedDisposable(tabActionBar, toDisposable(insert(this.tabActionBars, tabActionBar))); + const tabActionBarDisposable = combinedDisposable(tabActionBar, tabActionListener, toDisposable(insert(this.tabActionBars, tabActionBar))); // Tab Border Bottom const tabBorderBottomContainer = document.createElement('div'); diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts index 6a1743a35f9..36a58fb81ba 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts @@ -25,7 +25,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestWorkspaceTrustManagementService } from 'vs/workbench/test/common/workbenchTestServices'; import { TestExtensionTipsService, TestSharedProcessService } from 'vs/workbench/test/electron-sandbox/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; @@ -52,7 +52,6 @@ import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/u import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { platform } from 'vs/base/common/platform'; import { arch } from 'vs/base/common/process'; @@ -140,7 +139,7 @@ function setupTest() { instantiationService.stub(IUserDataSyncEnablementService, instantiationService.createInstance(UserDataSyncEnablementService)); instantiationService.set(IExtensionsWorkbenchService, disposables.add(instantiationService.createInstance(ExtensionsWorkbenchService))); - instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService()); + instantiationService.stub(IWorkspaceTrustManagementService, disposables.add(new TestWorkspaceTrustManagementService())); } diff --git a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts index 87691ff1fa3..1f89d3dc33c 100644 --- a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts @@ -5,10 +5,10 @@ import * as assert from 'assert'; import { Event } from 'vs/base/common/event'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { TestFilesConfigurationService, workbenchInstantiationService, TestServiceAccessor, registerTestFileEditor, createEditorPart, TestEnvironmentService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; @@ -43,19 +43,19 @@ suite('EditorAutoSave', () => { configurationService.setUserConfiguration('files', autoSaveConfig); instantiationService.stub(IConfigurationService, configurationService); - instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService, new TestContextService(TestWorkspace), TestEnvironmentService, - new UriIdentityService(new TestFileService()), - new TestFileService() - )); + disposables.add(new UriIdentityService(disposables.add(new TestFileService()))), + disposables.add(new TestFileService()) + ))); const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); const accessor = instantiationService.createInstance(TestServiceAccessor); @@ -71,14 +71,14 @@ suite('EditorAutoSave', () => { const resource = toResource.call(this, '/path/index.txt'); - const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; - model.textEditorModel.setValue('Super Good'); + const model: ITextFileEditorModel = disposables.add(await accessor.textFileService.files.resolve(resource)); + model.textEditorModel?.setValue('Super Good'); assert.ok(model.isDirty()); await awaitModelSaved(model); - assert.ok(!model.isDirty()); + assert.strictEqual(model.isDirty(), false); }); test('editor auto saves on focus change if configured', async function () { @@ -87,19 +87,23 @@ suite('EditorAutoSave', () => { const resource = toResource.call(this, '/path/index.txt'); await accessor.editorService.openEditor({ resource, options: { override: DEFAULT_EDITOR_ASSOCIATION.id } }); - const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; - model.textEditorModel.setValue('Super Good'); + const model: ITextFileEditorModel = disposables.add(await accessor.textFileService.files.resolve(resource)); + model.textEditorModel?.setValue('Super Good'); assert.ok(model.isDirty()); - await accessor.editorService.openEditor({ resource: toResource.call(this, '/path/index_other.txt') }); + const editorPane = await accessor.editorService.openEditor({ resource: toResource.call(this, '/path/index_other.txt') }); await awaitModelSaved(model); - assert.ok(!model.isDirty()); + assert.strictEqual(model.isDirty(), false); + + await editorPane?.group?.closeAllEditors(); }); function awaitModelSaved(model: ITextFileEditorModel): Promise { return Event.toPromise(Event.once(model.onDidChangeDirty)); } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index d66d44e14bb..c9094af17fb 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -25,7 +25,7 @@ import { TextEditorService } from 'vs/workbench/services/textfile/common/textEdi suite('Files - FileEditorInput', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -44,7 +44,6 @@ suite('Files - FileEditorInput', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService({ textEditorService: instantiationService => instantiationService.createInstance(TestTextEditorService) }, disposables); @@ -53,7 +52,7 @@ suite('Files - FileEditorInput', () => { }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('Basics', async function () { diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts index b0afe7cc554..ff9eb5b4383 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts @@ -6,9 +6,9 @@ import * as assert from 'assert'; import { Event } from 'vs/base/common/event'; import { TextFileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/textFileEditorTracker'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { workbenchInstantiationService, TestServiceAccessor, TestFilesConfigurationService, registerTestFileEditor, registerTestResourceEditor, createEditorPart, TestEnvironmentService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestFilesConfigurationService, registerTestFileEditor, registerTestResourceEditor, createEditorPart, TestEnvironmentService, TestFileService, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { IResolvedTextFileEditorModel, snapshotToString, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { FileChangesEvent, FileChangeType, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -25,8 +25,6 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files'; -import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -52,7 +50,7 @@ suite('Files - TextFileEditorTracker', () => { disposables.clear(); }); - async function createTracker(autoSaveEnabled = false): Promise { + async function createTracker(autoSaveEnabled = false): Promise<{ accessor: TestServiceAccessor; cleanup: () => Promise }> { const instantiationService = workbenchInstantiationService(undefined, disposables); if (autoSaveEnabled) { @@ -61,22 +59,22 @@ suite('Files - TextFileEditorTracker', () => { instantiationService.stub(IConfigurationService, configurationService); - instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + const fileService = disposables.add(new TestFileService()); + + instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService, new TestContextService(TestWorkspace), TestEnvironmentService, - new UriIdentityService(new TestFileService()), - new TestFileService() - )); + disposables.add(new UriIdentityService(fileService)), + fileService + ))); } const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); disposables.add(editorService); instantiationService.stub(IEditorService, editorService); @@ -85,11 +83,16 @@ suite('Files - TextFileEditorTracker', () => { disposables.add(instantiationService.createInstance(TestTextFileEditorTracker)); - return accessor; + const cleanup = async () => { + await workbenchTeardown(instantiationService); + part.dispose(); + }; + + return { accessor, cleanup }; } test('file change event updates model', async function () { - const accessor = await createTracker(); + const { accessor, cleanup } = await createTracker(); const resource = toResource.call(this, '/path/index.txt'); @@ -107,6 +110,8 @@ suite('Files - TextFileEditorTracker', () => { await timeout(0); // due to event updating model async assert.strictEqual(snapshotToString(model.createSnapshot()!), 'Hello Html'); + + await cleanup(); }); test('dirty text file model opens as editor', async function () { @@ -134,7 +139,7 @@ suite('Files - TextFileEditorTracker', () => { }); async function testDirtyTextFileModelOpensEditorDependingOnAutoSaveSetting(resource: URI, autoSave: boolean, error: boolean): Promise { - const accessor = await createTracker(autoSave); + const { accessor, cleanup } = await createTracker(autoSave); assert.ok(!accessor.editorService.isOpened({ resource, typeId: FILE_EDITOR_INPUT_ID, editorId: DEFAULT_EDITOR_ASSOCIATION.id })); @@ -159,6 +164,8 @@ suite('Files - TextFileEditorTracker', () => { await awaitEditorOpening(accessor.editorService); assert.ok(accessor.editorService.isOpened({ resource, typeId: FILE_EDITOR_INPUT_ID, editorId: DEFAULT_EDITOR_ASSOCIATION.id })); } + + await cleanup(); } test('dirty untitled text file model opens as editor', function () { @@ -170,7 +177,7 @@ suite('Files - TextFileEditorTracker', () => { }); async function testUntitledEditor(autoSaveEnabled: boolean): Promise { - const accessor = await createTracker(autoSaveEnabled); + const { accessor, cleanup } = await createTracker(autoSaveEnabled); const untitledTextEditor = await accessor.textEditorService.resolveTextEditor({ resource: undefined, forceUntitled: true }) as UntitledTextEditorInput; const model = disposables.add(await untitledTextEditor.resolve()); @@ -181,6 +188,8 @@ suite('Files - TextFileEditorTracker', () => { await awaitEditorOpening(accessor.editorService); assert.ok(accessor.editorService.isOpened(untitledTextEditor)); + + await cleanup(); } function awaitEditorOpening(editorService: IEditorService): Promise { @@ -188,7 +197,7 @@ suite('Files - TextFileEditorTracker', () => { } test('non-dirty files reload on window focus', async function () { - const accessor = await createTracker(); + const { accessor, cleanup } = await createTracker(); const resource = toResource.call(this, '/path/index.txt'); @@ -198,6 +207,8 @@ suite('Files - TextFileEditorTracker', () => { accessor.hostService.setFocus(true); await awaitModelResolveEvent(accessor.textFileService, resource); + + await cleanup(); }); function awaitModelResolveEvent(textFileService: ITextFileService, resource: URI): Promise { @@ -210,4 +221,6 @@ suite('Files - TextFileEditorTracker', () => { }); }); } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index c7bcfcf6d3b..fb7f0a56f29 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -58,9 +58,8 @@ import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookO import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestStorageService, TestWorkspaceTrustRequestService } from 'vs/workbench/test/common/workbenchTestServices'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { EditorFontLigatures, EditorFontVariations } from 'vs/editor/common/config/editorOptions'; @@ -189,7 +188,7 @@ export function setupInstantiationService(disposables = new DisposableStore()) { instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IClipboardService, TestClipboardService); instantiationService.stub(IStorageService, new TestStorageService()); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(true)); + instantiationService.stub(IWorkspaceTrustRequestService, disposables.add(new TestWorkspaceTrustRequestService(true))); instantiationService.stub(INotebookExecutionStateService, new TestNotebookExecutionStateService()); instantiationService.stub(IKeybindingService, new MockKeybindingService()); instantiationService.stub(INotebookCellStatusBarService, new NotebookCellStatusBarService()); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index b0ad29a3597..5b3a558fba2 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -431,7 +431,7 @@ export class TestingExplorerView extends ViewPane { this.dimensions.height = height; this.dimensions.width = width; this.container.style.height = `${height}px`; - this.viewModel.layout(height - this.treeHeader.clientHeight, width); + this.viewModel?.layout(height - this.treeHeader.clientHeight, width); this.filter.value?.layout(width); } } diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index ae5ee65a0b7..f67808e1483 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -8,7 +8,7 @@ import { EditorActivation, IResourceEditorInput } from 'vs/platform/editor/commo import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { DEFAULT_EDITOR_ASSOCIATION, EditorCloseContext, EditorsOrder, IEditorCloseEvent, EditorInputWithOptions, IEditorPane, IResourceDiffEditorInput, isEditorInputWithOptions, IUntitledTextResourceEditorInput, IUntypedEditorInput, SideBySideEditor, isEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; -import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput, ITestInstantiationService, registerTestResourceEditor, registerTestSideBySideEditor, createEditorPart, registerTestFileEditor, TestTextFileEditor, TestSingletonFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput, ITestInstantiationService, registerTestResourceEditor, registerTestSideBySideEditor, createEditorPart, registerTestFileEditor, TestTextFileEditor, TestSingletonFileEditorInput, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorGroup, IEditorGroupsService, GroupDirection, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; @@ -20,8 +20,7 @@ import { FileOperationEvent, FileOperation } from 'vs/platform/files/common/file import { DisposableStore } from 'vs/base/common/lifecycle'; import { MockScopableContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; -import { IWorkspaceTrustRequestService, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; +import { WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { ErrorPlaceholderEditor } from 'vs/workbench/browser/parts/editor/editorPlaceholder'; @@ -50,9 +49,7 @@ suite('EditorService', () => { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); return [part, editorService, instantiationService.createInstance(TestServiceAccessor)]; @@ -589,13 +586,7 @@ suite('EditorService', () => { lastUntitledEditorFactoryEditor = undefined; lastDiffEditorFactoryEditor = undefined; - for (const group of part.groups) { - await group.closeAllEditors(); - } - - for (const group of part.groups) { - accessor.editorGroupService.removeGroup(group); - } + await workbenchTeardown(accessor.instantiationService); rootGroup = part.activeGroup; } diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 2faa51fa86b..bbfd1d8437a 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -30,12 +30,12 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { mock } from 'vs/base/test/common/mock'; import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustEnablementService, TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { ExtensionManifestPropertiesService, IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; -import { TestContextService, TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestProductService, TestWorkspaceTrustEnablementService, TestWorkspaceTrustManagementService } from 'vs/workbench/test/common/workbenchTestServices'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; import { NullLogService } from 'vs/platform/log/common/log'; +import { DisposableStore } from 'vs/base/common/lifecycle'; function createStorageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -70,7 +70,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { }, null, null)); const extensionManagementService = instantiationService.createInstance(ExtensionManagementService); const workbenchExtensionManagementService = instantiationService.get(IWorkbenchExtensionManagementService) || instantiationService.stub(IWorkbenchExtensionManagementService, extensionManagementService); - const workspaceTrustManagementService = instantiationService.get(IWorkspaceTrustManagementService) || instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService()); + const disposables = new DisposableStore(); + const workspaceTrustManagementService = instantiationService.get(IWorkspaceTrustManagementService) || instantiationService.stub(IWorkspaceTrustManagementService, disposables.add(new TestWorkspaceTrustManagementService())); super( storageService, new GlobalExtensionEnablementService(storageService, extensionManagementService), @@ -90,6 +91,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IExtensionManifestPropertiesService) || instantiationService.stub(IExtensionManifestPropertiesService, new ExtensionManifestPropertiesService(TestProductService, new TestConfigurationService(), new TestWorkspaceTrustEnablementService(), new NullLogService())), instantiationService ); + this._register(disposables); } public async waitUntilInitialized(): Promise { diff --git a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts index 07e5b50f154..1924d01fe44 100644 --- a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts +++ b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts @@ -7,12 +7,11 @@ import * as assert from 'assert'; import { IExtensionManifest, ExtensionUntrustedWorkspaceSupportType } from 'vs/platform/extensions/common/extensions'; import { ExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestProductService, TestWorkspaceTrustEnablementService } from 'vs/workbench/test/common/workbenchTestServices'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; import { isWeb } from 'vs/base/common/platform'; -import { TestWorkspaceTrustEnablementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IWorkspaceTrustEnablementService } from 'vs/platform/workspace/common/workspaceTrust'; import { NullLogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/services/history/test/browser/historyService.test.ts b/src/vs/workbench/services/history/test/browser/historyService.test.ts index db24e25d864..659a066aacf 100644 --- a/src/vs/workbench/services/history/test/browser/historyService.test.ts +++ b/src/vs/workbench/services/history/test/browser/historyService.test.ts @@ -40,7 +40,7 @@ suite('HistoryService', function () { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); const configurationService = new TestConfigurationService(); diff --git a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts index 34604db9ea4..8561266eae5 100644 --- a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts +++ b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts @@ -12,7 +12,7 @@ import { workbenchInstantiationService } from 'vs/workbench/test/electron-sandbo suite('Lifecycleservice', function () { let lifecycleService: TestLifecycleService; - let disposables: DisposableStore; + const disposables = new DisposableStore(); class TestLifecycleService extends NativeLifecycleService { @@ -26,14 +26,12 @@ suite('Lifecycleservice', function () { } setup(async () => { - disposables = new DisposableStore(); - const instantiationService = workbenchInstantiationService(undefined, disposables); lifecycleService = instantiationService.createInstance(TestLifecycleService); }); teardown(async () => { - disposables.dispose(); + disposables.clear(); }); test('onBeforeShutdown - final veto called after other vetos', async function () { diff --git a/src/vs/workbench/services/storage/test/browser/storageService.test.ts b/src/vs/workbench/services/storage/test/browser/storageService.test.ts index 6653a3efbd1..25472109405 100644 --- a/src/vs/workbench/services/storage/test/browser/storageService.test.ts +++ b/src/vs/workbench/services/storage/test/browser/storageService.test.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { IStorageChangeEvent, Storage } from 'vs/base/parts/storage/common/storage'; import { flakySuite } from 'vs/base/test/common/testUtils'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -49,7 +50,7 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS cacheHome: joinPath(inMemoryExtraProfileRoot, 'cache') }; - const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, new UserDataProfileService(inMemoryExtraProfile, new UserDataProfilesService(TestEnvironmentService, fileService, new UriIdentityService(fileService), logService)), logService)); + const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, disposables.add(new UserDataProfileService(inMemoryExtraProfile, new UserDataProfilesService(TestEnvironmentService, fileService, disposables.add(new UriIdentityService(fileService)), logService))), logService)); await storageService.initialize(); @@ -73,6 +74,8 @@ flakySuite('StorageService (browser)', function () { disposables.clear(); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); flakySuite('StorageService (browser specific)', () => { @@ -110,6 +113,8 @@ flakySuite('StorageService (browser specific)', () => { } }); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); flakySuite('IndexDBStorageDatabase (browser)', () => { @@ -117,13 +122,17 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { const id = 'workspace-storage-db-test'; const logService = new NullLogService(); + const disposables = new DisposableStore(); + teardown(async () => { - const storage = await IndexedDBStorageDatabase.create({ id }, logService); + const storage = disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)); await storage.clear(); + + disposables.clear(); }); test('Basics', async () => { - let storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + let storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -145,7 +154,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -168,7 +177,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -196,7 +205,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -209,7 +218,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { }); test('Clear', async () => { - let storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + let storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -219,13 +228,13 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - const db = await IndexedDBStorageDatabase.create({ id }, logService); - storage = new Storage(db); + const db = disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(db)); await storage.init(); await db.clear(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -238,7 +247,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { }); test('Inserts and Deletes at the same time', async () => { - let storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + let storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -248,7 +257,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -260,7 +269,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -271,9 +280,9 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { }); test('Storage change event', async () => { - const storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + const storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); let storageChangeEvents: IStorageChangeEvent[] = []; - storage.onDidChangeStorage(e => storageChangeEvents.push(e)); + disposables.add(storage.onDidChangeStorage(e => storageChangeEvents.push(e))); await storage.init(); @@ -294,4 +303,6 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { storageValueChangeEvent = storageChangeEvents.find(e => e.key === 'isExternal'); strictEqual(storageValueChangeEvent?.external, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts b/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts index 58d93d4b786..4ae038ffe92 100644 --- a/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts @@ -22,6 +22,7 @@ import { isWeb } from 'vs/base/common/platform'; import { IWorkingCopyFileService, WorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { WorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; // optimization: we don't need to run this suite in native environment, // because we have nativeTextFileService.io.test.ts for it, @@ -39,18 +40,17 @@ if (isWeb) { const instantiationService = workbenchInstantiationService(undefined, disposables); const logService = new NullLogService(); - const fileService = new FileService(logService); + const fileService = disposables.add(new FileService(logService)); - fileProvider = new TestInMemoryFileSystemProvider(); + fileProvider = disposables.add(new TestInMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); + collection.set(IWorkingCopyFileService, disposables.add(new WorkingCopyFileService(fileService, disposables.add(new WorkingCopyService()), instantiationService, disposables.add(new UriIdentityService(fileService))))); - collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new WorkingCopyService(), instantiationService, new UriIdentityService(fileService))); - - service = instantiationService.createChild(collection).createInstance(TestBrowserTextFileServiceWithEncodingOverrides); + service = disposables.add(instantiationService.createChild(collection).createInstance(TestBrowserTextFileServiceWithEncodingOverrides)); + disposables.add(service.files); await fileProvider.mkdir(URI.file(testDir)); for (const fileName in files) { @@ -65,8 +65,6 @@ if (isWeb) { }, teardown: async () => { - (service.files).dispose(); - disposables.clear(); }, @@ -111,5 +109,7 @@ if (isWeb) { return null; // ignore errors (like file not found) } } + + ensureNoDisposablesAreLeakedInTestSuite(); }); } diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 7ca2a09bfb1..c0b6d62e26e 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -28,22 +28,21 @@ suite('Files - TextFileEditorModel', () => { return stat ? stat.mtime : -1; } - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let content: string; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); content = accessor.fileService.getContent(); + disposables.add(accessor.textFileService.files); }); teardown(() => { - (accessor.textFileService.files).dispose(); accessor.fileService.setContent(content); - disposables.dispose(); + disposables.clear(); }); test('basic events', async function () { diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts index 3a46ef121f9..71b9b4c9957 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts @@ -18,18 +18,17 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Files - TextFileEditorModelManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('add, remove, clear, get, getAll', function () { diff --git a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts index 45bd9ba71fd..a16c6693c1e 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts @@ -13,21 +13,20 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Files - TextFileService', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let model: TextFileEditorModel; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.textFileService.files); }); teardown(() => { model?.dispose(); - (accessor.textFileService.files).dispose(); - disposables.dispose(); + disposables.clear(); }); test('isDirty/getDirty - files and untitled', async function () { diff --git a/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts index 3bc8637e4aa..02f4f1e4bc2 100644 --- a/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts @@ -13,6 +13,7 @@ import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { ITextSnapshot, DefaultEndOfLine } from 'vs/editor/common/model'; import { isWindows } from 'vs/base/common/platform'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export interface Params { setup(): Promise<{ @@ -40,6 +41,7 @@ export default function createSuite(params: Params) { let service: ITextFileService; let testDir = ''; const { exists, stat, readFile, detectEncodingByBOM } = params; + const disposables = new DisposableStore(); setup(async () => { const result = await params.setup(); @@ -49,6 +51,7 @@ export default function createSuite(params: Params) { teardown(async () => { await params.teardown(); + disposables.clear(); }); test('create - no encoding - content empty', async () => { @@ -165,9 +168,9 @@ export default function createSuite(params: Params) { }); function createTextModelSnapshot(text: string, preserveBOM?: boolean): ITextSnapshot { - const textModel = createTextModel(text); + const textModel = disposables.add(createTextModel(text)); const snapshot = textModel.createSnapshot(preserveBOM); - textModel.dispose(); + return snapshot; } @@ -224,7 +227,8 @@ export default function createSuite(params: Params) { const resolved = await service.readStream(resource); assert.strictEqual(resolved.encoding, encoding); - assert.strictEqual(snapshotToString(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer.createSnapshot(false)), expectedContent); + const textBuffer = disposables.add(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer); + assert.strictEqual(snapshotToString(textBuffer.createSnapshot(false)), expectedContent); } test('write - use encoding (cp1252)', async () => { @@ -252,18 +256,21 @@ export default function createSuite(params: Params) { async function testEncodingKeepsData(resource: URI, encoding: string, expected: string) { let resolved = await service.readStream(resource, { encoding }); - const content = snapshotToString(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer.createSnapshot(false)); + const textBuffer = disposables.add(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer); + const content = snapshotToString(textBuffer.createSnapshot(false)); assert.strictEqual(content, expected); await service.write(resource, content, { encoding }); resolved = await service.readStream(resource, { encoding }); - assert.strictEqual(snapshotToString(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer.createSnapshot(false)), content); + const textBuffer2 = disposables.add(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer); + assert.strictEqual(snapshotToString(textBuffer2.createSnapshot(false)), content); await service.write(resource, createTextModelSnapshot(content), { encoding }); resolved = await service.readStream(resource, { encoding }); - assert.strictEqual(snapshotToString(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer.createSnapshot(false)), content); + const textBuffer3 = disposables.add(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer); + assert.strictEqual(snapshotToString(textBuffer3.createSnapshot(false)), content); } test('write - no encoding - content as string', async () => { @@ -340,7 +347,7 @@ export default function createSuite(params: Params) { let detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.strictEqual(detectedEncoding, null); - const model = createTextModel((await readFile(resource.fsPath)).toString() + 'updates'); + const model = disposables.add(createTextModel((await readFile(resource.fsPath)).toString() + 'updates')); await service.write(resource, model.createSnapshot(), { encoding: UTF8_with_bom }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); @@ -360,8 +367,6 @@ export default function createSuite(params: Params) { await service.write(resource, model.createSnapshot(), { encoding: UTF8 }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.strictEqual(detectedEncoding, null); - - model.dispose(); }); test('write - preserve UTF8 BOM - content as string', async () => { @@ -412,8 +417,9 @@ export default function createSuite(params: Params) { assert.strictEqual(result.size, (await stat(resource.fsPath)).size); const content = (await readFile(resource.fsPath)).toString(); + const textBuffer = disposables.add(result.value.create(DefaultEndOfLine.LF).textBuffer); assert.strictEqual( - snapshotToString(result.value.create(DefaultEndOfLine.LF).textBuffer.createSnapshot(false)), + snapshotToString(textBuffer.createSnapshot(false)), snapshotToString(createTextModelSnapshot(content, false))); } @@ -541,7 +547,8 @@ export default function createSuite(params: Params) { const result = await service.readStream(resource, { encoding }); assert.strictEqual(result.encoding, encoding); - let contents = snapshotToString(result.value.create(DefaultEndOfLine.LF).textBuffer.createSnapshot(false)); + const textBuffer = disposables.add(result.value.create(DefaultEndOfLine.LF).textBuffer); + let contents = snapshotToString(textBuffer.createSnapshot(false)); assert.strictEqual(contents.indexOf(needle), 0); assert.ok(contents.indexOf(needle, 10) > 0); @@ -557,7 +564,8 @@ export default function createSuite(params: Params) { const factory = await createTextBufferFactoryFromStream(await service.getDecodedStream(resource, bufferToStream(rawFileVSBuffer), { encoding })); - contents = snapshotToString(factory.create(DefaultEndOfLine.LF).textBuffer.createSnapshot(false)); + const textBuffer2 = disposables.add(factory.create(DefaultEndOfLine.LF).textBuffer); + contents = snapshotToString(textBuffer2.createSnapshot(false)); assert.strictEqual(contents.indexOf(needle), 0); assert.ok(contents.indexOf(needle, 10) > 0); diff --git a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts index 707780e4c2a..94864bb3094 100644 --- a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts @@ -35,18 +35,17 @@ suite('Files - NativeTextFileService i/o', function () { const instantiationService = workbenchInstantiationService(undefined, disposables); const logService = new NullLogService(); - const fileService = new FileService(logService); + const fileService = disposables.add(new FileService(logService)); - fileProvider = new TestInMemoryFileSystemProvider(); + fileProvider = disposables.add(new TestInMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); + collection.set(IWorkingCopyFileService, disposables.add(new WorkingCopyFileService(fileService, disposables.add(new WorkingCopyService()), instantiationService, disposables.add(new UriIdentityService(fileService))))); - collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new WorkingCopyService(), instantiationService, new UriIdentityService(fileService))); - - service = instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides); + service = disposables.add(instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides)); + disposables.add(service.files); await fileProvider.mkdir(URI.file(testDir)); for (const fileName in files) { @@ -61,8 +60,6 @@ suite('Files - NativeTextFileService i/o', function () { }, teardown: async () => { - (service.files).dispose(); - disposables.clear(); }, diff --git a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts index 1ac327fd20c..a48728e4a7a 100644 --- a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts @@ -19,7 +19,7 @@ import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentitySe import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; suite('Files - NativeTextFileService', function () { const disposables = new DisposableStore(); @@ -31,28 +31,25 @@ suite('Files - NativeTextFileService', function () { instantiationService = workbenchInstantiationService(undefined, disposables); const logService = new NullLogService(); - const fileService = new FileService(logService); + const fileService = disposables.add(new FileService(logService)); - const fileProvider = new InMemoryFileSystemProvider(); + const fileProvider = disposables.add(new InMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); + collection.set(IWorkingCopyFileService, disposables.add(new WorkingCopyFileService(fileService, disposables.add(new WorkingCopyService()), instantiationService, disposables.add(new UriIdentityService(fileService))))); - collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new WorkingCopyService(), instantiationService, new UriIdentityService(fileService))); - - service = instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides); + service = disposables.add(instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides)); + disposables.add(service.files); }); teardown(() => { - (service.files).dispose(); - disposables.clear(); }); test('shutdown joins on pending saves', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined)); await model.resolve(); @@ -67,4 +64,6 @@ suite('Files - NativeTextFileService', function () { assert.strictEqual(pendingSaveAwaited, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts index 9d7af92be98..4ccf202e643 100644 --- a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts @@ -22,21 +22,18 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Workbench - TextModelResolverService', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; - let model: TextFileEditorModel; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.textFileService.files); }); teardown(() => { - model?.dispose(); - (accessor.textFileService.files).dispose(); - disposables.dispose(); + disposables.clear(); }); test('resolve resource', async () => { diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 935b78a3dfd..8a0c6dc8529 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -24,19 +24,18 @@ import { LanguageDetectionLanguageEventSource } from 'vs/workbench/services/lang suite('Untitled text editors', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.untitledTextEditorService as UntitledTextEditorService); }); teardown(() => { - (accessor.untitledTextEditorService as UntitledTextEditorService).dispose(); - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts index f9ec56ad11a..16715e8b21d 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts @@ -116,7 +116,7 @@ export class WorkingCopyBackupsModel { } } -export abstract class WorkingCopyBackupService implements IWorkingCopyBackupService { +export abstract class WorkingCopyBackupService extends Disposable implements IWorkingCopyBackupService { declare readonly _serviceBrand: undefined; @@ -127,7 +127,9 @@ export abstract class WorkingCopyBackupService implements IWorkingCopyBackupServ @IFileService protected fileService: IFileService, @ILogService private readonly logService: ILogService ) { - this.impl = this.initialize(backupWorkspaceHome); + super(); + + this.impl = this._register(this.initialize(backupWorkspaceHome)); } private initialize(backupWorkspaceHome: URI | undefined): WorkingCopyBackupServiceImpl | InMemoryWorkingCopyBackupService { @@ -533,13 +535,15 @@ class WorkingCopyBackupServiceImpl extends Disposable implements IWorkingCopyBac } } -export class InMemoryWorkingCopyBackupService implements IWorkingCopyBackupService { +export class InMemoryWorkingCopyBackupService extends Disposable implements IWorkingCopyBackupService { declare readonly _serviceBrand: undefined; private backups = new ResourceMap<{ typeId: string; content: VSBuffer; meta?: IWorkingCopyBackupMeta }>(); - constructor() { } + constructor() { + super(); + } async hasBackups(): Promise { return this.backups.size > 0; diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts b/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts index 8775ac71554..8e0f773f30c 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; -import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IWorkingCopy, IWorkingCopyIdentifier, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { ILogService } from 'vs/platform/log/common/log'; @@ -56,8 +56,8 @@ export abstract class WorkingCopyBackupTracker extends Disposable { this._register(this.workingCopyService.onDidChangeContent(workingCopy => this.onDidChangeContent(workingCopy))); // Lifecycle - this.lifecycleService.onBeforeShutdown(event => (event as InternalBeforeShutdownEvent).finalVeto(() => this.onFinalBeforeShutdown(event.reason), 'veto.backups')); - this.lifecycleService.onWillShutdown(() => this.onWillShutdown()); + this._register(this.lifecycleService.onBeforeShutdown(event => (event as InternalBeforeShutdownEvent).finalVeto(() => this.onFinalBeforeShutdown(event.reason), 'veto.backups'))); + this._register(this.lifecycleService.onWillShutdown(() => this.onWillShutdown())); // Once a handler registers, restore backups this._register(this.workingCopyEditorService.onDidRegisterHandler(handler => this.restoreBackups(handler))); @@ -103,8 +103,8 @@ export abstract class WorkingCopyBackupTracker extends Disposable { // A map of scheduled pending backup operations for working copies // Given https://github.com/microsoft/vscode/issues/158038, we explicitly // do not store `IWorkingCopy` but the identifier in the map, since it - // looks like GC is not runnin for the working copy otherwise. - protected readonly pendingBackupOperations = new Map(); + // looks like GC is not running for the working copy otherwise. + protected readonly pendingBackupOperations = new Map void }>(); private suspended = false; @@ -206,17 +206,22 @@ export abstract class WorkingCopyBackupTracker extends Disposable { // Clear disposable unless we got canceled which would // indicate another operation has started meanwhile if (!cts.token.isCancellationRequested) { - this.pendingBackupOperations.delete(workingCopyIdentifier); + this.doClearPendingBackupOperation(workingCopyIdentifier); } }, this.getBackupScheduleDelay(workingCopy)); // Keep in map for disposal as needed - this.pendingBackupOperations.set(workingCopyIdentifier, toDisposable(() => { - this.logService.trace(`[backup tracker] clearing pending backup creation`, workingCopy.resource.toString(), workingCopy.typeId); + this.pendingBackupOperations.set(workingCopyIdentifier, { + cancel: () => { + this.logService.trace(`[backup tracker] clearing pending backup creation`, workingCopy.resource.toString(), workingCopy.typeId); - cts.dispose(true); - clearTimeout(handle); - })); + cts.cancel(); + }, + disposable: toDisposable(() => { + cts.dispose(); + clearTimeout(handle); + }) + }); } protected getBackupScheduleDelay(workingCopy: IWorkingCopy): number { @@ -247,11 +252,14 @@ export abstract class WorkingCopyBackupTracker extends Disposable { this.doDiscardBackup(workingCopyIdentifier, cts); // Keep in map for disposal as needed - this.pendingBackupOperations.set(workingCopyIdentifier, toDisposable(() => { - this.logService.trace(`[backup tracker] clearing pending backup discard`, workingCopy.resource.toString(), workingCopy.typeId); + this.pendingBackupOperations.set(workingCopyIdentifier, { + cancel: () => { + this.logService.trace(`[backup tracker] clearing pending backup discard`, workingCopy.resource.toString(), workingCopy.typeId); - cts.dispose(true); - })); + cts.cancel(); + }, + disposable: cts + }); } private async doDiscardBackup(workingCopyIdentifier: IWorkingCopyIdentifier, cts: CancellationTokenSource) { @@ -267,7 +275,7 @@ export abstract class WorkingCopyBackupTracker extends Disposable { // Clear disposable unless we got canceled which would // indicate another operation has started meanwhile if (!cts.token.isCancellationRequested) { - this.pendingBackupOperations.delete(workingCopyIdentifier); + this.doClearPendingBackupOperation(workingCopyIdentifier); } } @@ -287,14 +295,29 @@ export abstract class WorkingCopyBackupTracker extends Disposable { } if (workingCopyIdentifier) { - dispose(this.pendingBackupOperations.get(workingCopyIdentifier)); - this.pendingBackupOperations.delete(workingCopyIdentifier); + this.doClearPendingBackupOperation(workingCopyIdentifier, { cancel: true }); } } + private doClearPendingBackupOperation(workingCopyIdentifier: IWorkingCopyIdentifier, options?: { cancel: boolean }): void { + const pendingBackupOperation = this.pendingBackupOperations.get(workingCopyIdentifier); + if (!pendingBackupOperation) { + return; + } + + if (options?.cancel) { + pendingBackupOperation.cancel(); + } + + pendingBackupOperation.disposable.dispose(); + + this.pendingBackupOperations.delete(workingCopyIdentifier); + } + protected cancelBackupOperations(): void { - for (const [, disposable] of this.pendingBackupOperations) { - dispose(disposable); + for (const [, operation] of this.pendingBackupOperations) { + operation.cancel(); + operation.disposable.dispose(); } this.pendingBackupOperations.clear(); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts index a689d217c5e..56e3fd5e9cd 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts @@ -814,7 +814,7 @@ export class NativeWorkingCopyHistoryService extends WorkingCopyHistoryService { if (!this.isRemotelyStored) { // Local: persist all on shutdown - this.lifecycleService.onWillShutdown(e => this.onWillShutdown(e)); + this._register(this.lifecycleService.onWillShutdown(e => this.onWillShutdown(e))); // Local: schedule persist on change this._register(Event.any(this.onDidAddEntry, this.onDidChangeEntry, this.onDidReplaceEntry, this.onDidRemoveEntry)(() => this.onDidChangeModels())); diff --git a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts index 1aa006ca6e6..5e597eaa2bc 100644 --- a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts @@ -34,7 +34,7 @@ export class NativeWorkingCopyBackupService extends WorkingCopyBackupService { // Lifecycle: ensure to prolong the shutdown for as long // as pending backup operations have not finished yet. // Otherwise, we risk writing partial backups to disk. - this.lifecycleService.onWillShutdown(event => event.join(this.joinBackups(), { id: 'join.workingCopyBackups', label: localize('join.workingCopyBackups', "Backup working copies") })); + this._register(this.lifecycleService.onWillShutdown(event => event.join(this.joinBackups(), { id: 'join.workingCopyBackups', label: localize('join.workingCopyBackups', "Backup working copies") }))); } } diff --git a/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts index 307ed8eb379..c048929e21c 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts @@ -18,21 +18,20 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('FileWorkingCopyManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let manager: IFileWorkingCopyManager; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); accessor.fileService.registerProvider(Schemas.file, new TestInMemoryFileSystemProvider()); accessor.fileService.registerProvider(Schemas.vscodeRemote, new TestInMemoryFileSystemProvider()); - manager = new FileWorkingCopyManager( + manager = disposables.add(new FileWorkingCopyManager( 'testFileWorkingCopyType', new TestStoredFileWorkingCopyModelFactory(), new TestUntitledFileWorkingCopyModelFactory(), @@ -41,12 +40,11 @@ suite('FileWorkingCopyManager', () => { accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, accessor.environmentService, accessor.dialogService, accessor.decorationsService - ); + )); }); teardown(() => { - manager.dispose(); - disposables.dispose(); + disposables.clear(); }); test('onDidCreate, get, workingCopies', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts index b7f6501ab42..c745f135603 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts @@ -32,7 +32,7 @@ suite('ResourceWorkingCopy', function () { } - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.file('test/resource'); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -43,16 +43,14 @@ suite('ResourceWorkingCopy', function () { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('orphaned tracking', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts index df3106ac319..8d45d3c7ddf 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts @@ -129,7 +129,7 @@ suite('StoredFileWorkingCopy (with custom save)', function () { const factory = new TestStoredFileWorkingCopyModelWithCustomSaveFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.file('test/resource'); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -142,16 +142,14 @@ suite('StoredFileWorkingCopy (with custom save)', function () { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('save (custom implemented)', async () => { @@ -200,7 +198,7 @@ suite('StoredFileWorkingCopy', function () { const factory = new TestStoredFileWorkingCopyModelFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.file('test/resource'); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -213,16 +211,14 @@ suite('StoredFileWorkingCopy', function () { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('registers with working copy service', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts index cfb69f33e2e..08665525ef0 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts @@ -20,30 +20,28 @@ import { isWeb } from 'vs/base/common/platform'; suite('StoredFileWorkingCopyManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let manager: IStoredFileWorkingCopyManager; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - manager = new StoredFileWorkingCopyManager( + manager = disposables.add(new StoredFileWorkingCopyManager( 'testStoredFileWorkingCopyType', new TestStoredFileWorkingCopyModelFactory(), accessor.fileService, accessor.lifecycleService, accessor.labelService, accessor.logService, accessor.workingCopyFileService, accessor.workingCopyBackupService, accessor.uriIdentityService, accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService - ); + )); }); teardown(() => { - manager.dispose(); - disposables.dispose(); + disposables.clear(); }); test('resolve', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts index c98fad63dac..d38d34ce846 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts @@ -90,7 +90,7 @@ suite('UntitledFileWorkingCopy', () => { const factory = new TestUntitledFileWorkingCopyModelFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -113,16 +113,14 @@ suite('UntitledFileWorkingCopy', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('registers with working copy service', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts index c32b141d5f6..32799349d46 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts @@ -17,21 +17,20 @@ import { TestInMemoryFileSystemProvider, TestServiceAccessor, workbenchInstantia suite('UntitledFileWorkingCopyManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let manager: IFileWorkingCopyManager; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); accessor.fileService.registerProvider(Schemas.file, new TestInMemoryFileSystemProvider()); accessor.fileService.registerProvider(Schemas.vscodeRemote, new TestInMemoryFileSystemProvider()); - manager = new FileWorkingCopyManager( + manager = disposables.add(new FileWorkingCopyManager( 'testUntitledFileWorkingCopyType', new TestStoredFileWorkingCopyModelFactory(), new TestUntitledFileWorkingCopyModelFactory(), @@ -40,12 +39,11 @@ suite('UntitledFileWorkingCopyManager', () => { accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, accessor.environmentService, accessor.dialogService, accessor.decorationsService - ); + )); }); teardown(() => { - manager.dispose(); - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts index b3f94c4ab9c..501dd4dac49 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts @@ -27,7 +27,7 @@ suite('UntitledScratchpadWorkingCopy', () => { const factory = new TestUntitledFileWorkingCopyModelFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -50,16 +50,14 @@ suite('UntitledScratchpadWorkingCopy', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('registers with working copy service', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts index eab01d63a3b..ee95011f41f 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts @@ -24,13 +24,11 @@ import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices' import { CancellationToken } from 'vs/base/common/cancellation'; import { timeout } from 'vs/base/common/async'; import { BrowserWorkingCopyBackupTracker } from 'vs/workbench/services/workingCopy/browser/workingCopyBackupTracker'; -import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IWorkingCopyEditorHandler, IWorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService'; import { bufferToReadable, VSBuffer } from 'vs/base/common/buffer'; import { isWindows } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; -import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; suite('WorkingCopyBackupTracker (browser)', function () { let accessor: TestServiceAccessor; @@ -40,7 +38,15 @@ suite('WorkingCopyBackupTracker (browser)', function () { disposables.add(registerTestResourceEditor()); }); - teardown(() => { + teardown(async () => { + for (const copy of accessor.workingCopyService.workingCopies) { + await copy.revert(); + } + + for (const group of accessor.editorGroupService.groups) { + await group.closeAllEditors(); + } + disposables.clear(); }); @@ -85,10 +91,8 @@ suite('WorkingCopyBackupTracker (browser)', function () { } } - async function createTracker(): Promise<{ accessor: TestServiceAccessor; part: EditorPart; tracker: TestWorkingCopyBackupTracker; workingCopyBackupService: InMemoryTestWorkingCopyBackupService; instantiationService: IInstantiationService; cleanup: () => void }> { - const disposables = new DisposableStore(); - - const workingCopyBackupService = new InMemoryTestWorkingCopyBackupService(); + async function createTracker(): Promise<{ accessor: TestServiceAccessor; part: EditorPart; tracker: TestWorkingCopyBackupTracker; workingCopyBackupService: InMemoryTestWorkingCopyBackupService; instantiationService: IInstantiationService }> { + const workingCopyBackupService = disposables.add(new InMemoryTestWorkingCopyBackupService()); const instantiationService = workbenchInstantiationService(undefined, disposables); instantiationService.stub(IWorkingCopyBackupService, workingCopyBackupService); @@ -97,20 +101,18 @@ suite('WorkingCopyBackupTracker (browser)', function () { disposables.add(registerTestResourceEditor()); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); accessor = instantiationService.createInstance(TestServiceAccessor); const tracker = disposables.add(instantiationService.createInstance(TestWorkingCopyBackupTracker)); - return { accessor, part, tracker, workingCopyBackupService: workingCopyBackupService, instantiationService, cleanup: () => disposables.dispose() }; + return { accessor, part, tracker, workingCopyBackupService: workingCopyBackupService, instantiationService }; } async function untitledBackupTest(untitled: IUntitledTextResourceEditorInput = { resource: undefined }): Promise { - const { accessor, cleanup, workingCopyBackupService } = await createTracker(); + const { accessor, workingCopyBackupService } = await createTracker(); const untitledTextEditor = (await accessor.editorService.openEditor(untitled))?.input as UntitledTextEditorInput; @@ -129,8 +131,6 @@ suite('WorkingCopyBackupTracker (browser)', function () { await workingCopyBackupService.joinDiscardBackup(); assert.strictEqual(workingCopyBackupService.hasBackupSync(untitledTextModel), false); - - cleanup(); } test('Track backups (untitled)', function () { @@ -142,14 +142,14 @@ suite('WorkingCopyBackupTracker (browser)', function () { }); test('Track backups (custom)', async function () { - const { accessor, tracker, cleanup, workingCopyBackupService } = await createTracker(); + const { accessor, tracker, workingCopyBackupService } = await createTracker(); class TestBackupWorkingCopy extends TestWorkingCopy { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + disposables.add(accessor.workingCopyService.registerWorkingCopy(this)); } readonly backupDelay = 10; @@ -162,7 +162,7 @@ suite('WorkingCopyBackupTracker (browser)', function () { } const resource = toResource.call(this, '/path/custom.txt'); - const customWorkingCopy = new TestBackupWorkingCopy(resource); + const customWorkingCopy = disposables.add(new TestBackupWorkingCopy(resource)); // Normal customWorkingCopy.setDirty(true); @@ -188,29 +188,22 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.strictEqual(tracker.pendingBackupOperationCount, 1); await workingCopyBackupService.joinDiscardBackup(); assert.strictEqual(workingCopyBackupService.hasBackupSync(customWorkingCopy), false); - - customWorkingCopy.dispose(); - cleanup(); }); - async function restoreBackupsInit(): Promise<[TestWorkingCopyBackupTracker, TestServiceAccessor, IDisposable]> { + async function restoreBackupsInit(): Promise<[TestWorkingCopyBackupTracker, TestServiceAccessor]> { const fooFile = URI.file(isWindows ? 'c:\\Foo' : '/Foo'); const barFile = URI.file(isWindows ? 'c:\\Bar' : '/Bar'); const untitledFile1 = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); const untitledFile2 = URI.from({ scheme: Schemas.untitled, path: 'Untitled-2' }); - const disposables = new DisposableStore(); - - const workingCopyBackupService = new InMemoryTestWorkingCopyBackupService(); + const workingCopyBackupService = disposables.add(new InMemoryTestWorkingCopyBackupService()); const instantiationService = workbenchInstantiationService(undefined, disposables); instantiationService.stub(IWorkingCopyBackupService, workingCopyBackupService); const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); accessor = instantiationService.createInstance(TestServiceAccessor); @@ -230,11 +223,11 @@ suite('WorkingCopyBackupTracker (browser)', function () { accessor.lifecycleService.phase = LifecyclePhase.Restored; - return [tracker, accessor, disposables]; + return [tracker, accessor]; } test('Restore backups (basics, some handled)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); assert.strictEqual(tracker.getUnrestoredBackups().size, 0); @@ -256,7 +249,7 @@ suite('WorkingCopyBackupTracker (browser)', function () { createEditor: workingCopy => { createEditorCounter++; - return accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + return disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); } }); @@ -272,12 +265,10 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.ok(editor instanceof TestUntitledTextEditorInput); assert.strictEqual(editor.resolved, true); } - - dispose(disposables); }); test('Restore backups (basics, none handled)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); await tracker.testRestoreBackups({ handles: workingCopy => false, @@ -287,12 +278,10 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.strictEqual(accessor.editorService.count, 0); assert.strictEqual(tracker.getUnrestoredBackups().size, 4); - - dispose(disposables); }); test('Restore backups (basics, error case)', async function () { - const [tracker, , disposables] = await restoreBackupsInit(); + const [tracker] = await restoreBackupsInit(); try { await tracker.testRestoreBackups({ @@ -305,12 +294,10 @@ suite('WorkingCopyBackupTracker (browser)', function () { } assert.strictEqual(tracker.getUnrestoredBackups().size, 4); - - dispose(disposables); }); test('Restore backups (multiple handlers)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); const firstHandler = tracker.testRestoreBackups({ handles: workingCopy => { @@ -346,20 +333,18 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.ok(editor instanceof TestUntitledTextEditorInput); assert.strictEqual(editor.resolved, true); } - - dispose(disposables); }); test('Restore backups (editors already opened)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); assert.strictEqual(tracker.getUnrestoredBackups().size, 0); let handlesCounter = 0; let isOpenCounter = 0; - const editor1 = accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); - const editor2 = accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + const editor1 = disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); + const editor2 = disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); await accessor.editorService.openEditors([{ editor: editor1 }, { editor: editor2 }]); @@ -396,7 +381,5 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.strictEqual(editor.resolved, true); } } - - dispose(disposables); }); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts index 26521dee61a..11bad8713a6 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts @@ -6,12 +6,11 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; import { IWorkingCopyEditorHandler, WorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { createEditorPart, registerTestResourceEditor, TestEditorService, TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; @@ -28,12 +27,12 @@ suite('WorkingCopyEditorService', () => { }); test('registry - basics', () => { - const service = new WorkingCopyEditorService(new TestEditorService()); + const service = disposables.add(new WorkingCopyEditorService(new TestEditorService())); let handlerEvent: IWorkingCopyEditorHandler | undefined = undefined; - service.onDidRegisterHandler(handler => { + disposables.add(service.onDidRegisterHandler(handler => { handlerEvent = handler; - }); + })); const editorHandler: IWorkingCopyEditorHandler = { handles: workingCopy => false, @@ -41,11 +40,9 @@ suite('WorkingCopyEditorService', () => { createEditor: workingCopy => { throw new Error(); } }; - const disposable = service.registerHandler(editorHandler); + disposables.add(service.registerHandler(editorHandler)); assert.strictEqual(handlerEvent, editorHandler); - - disposable.dispose(); }); test('findEditor', async () => { @@ -55,14 +52,13 @@ suite('WorkingCopyEditorService', () => { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); const accessor = instantiationService.createInstance(TestServiceAccessor); - const service = new WorkingCopyEditorService(editorService); + const service = disposables.add(new WorkingCopyEditorService(editorService)); const resource = URI.parse('custom://some/folder/custom.txt'); - const testWorkingCopy = new TestWorkingCopy(resource, false, 'testWorkingCopyTypeId1'); + const testWorkingCopy = disposables.add(new TestWorkingCopy(resource, false, 'testWorkingCopyTypeId1')); assert.strictEqual(service.findEditor(testWorkingCopy), undefined); @@ -74,8 +70,8 @@ suite('WorkingCopyEditorService', () => { disposables.add(service.registerHandler(editorHandler)); - const editor1 = instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); - const editor2 = instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + const editor1 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); + const editor2 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); await editorService.openEditors([{ editor: editor1 }, { editor: editor2 }]); @@ -83,4 +79,6 @@ suite('WorkingCopyEditorService', () => { disposables.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts index ab966104874..14e3b835991 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts @@ -20,19 +20,18 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('WorkingCopyFileService', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.textFileService.files); }); teardown(() => { - (accessor.textFileService.files).dispose(); - disposables.dispose(); + disposables.clear(); }); test('create - dirty file', async function () { diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts index 8c381b4318b..92da56329ee 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts @@ -30,6 +30,8 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil import { generateUuid } from 'vs/base/common/uuid'; import { INativeWindowConfiguration } from 'vs/platform/window/common/window'; import product from 'vs/platform/product/common/product'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; const homeDir = URI.file('home').with({ scheme: Schemas.inMemory }); const tmpDir = URI.file('tmp').with({ scheme: Schemas.inMemory }); @@ -170,6 +172,8 @@ suite('WorkingCopyBackupService', () => { let service: NodeTestWorkingCopyBackupService; let fileService: IFileService; + const disposables = new DisposableStore(); + const workspaceResource = URI.file(isWindows ? 'c:\\workspace' : '/workspace'); const fooFile = URI.file(isWindows ? 'c:\\Foo' : '/Foo'); const customFile = URI.parse('customScheme://some/path'); @@ -184,7 +188,7 @@ suite('WorkingCopyBackupService', () => { workspacesJsonPath = joinPath(backupHome, 'workspaces.json'); workspaceBackupPath = joinPath(backupHome, hash(workspaceResource.fsPath).toString(16)); - service = new NodeTestWorkingCopyBackupService(testDir, workspaceBackupPath); + service = disposables.add(new NodeTestWorkingCopyBackupService(testDir, workspaceBackupPath)); fileService = service._fileService; await fileService.createFolder(backupHome); @@ -192,6 +196,10 @@ suite('WorkingCopyBackupService', () => { return fileService.writeFile(workspacesJsonPath, VSBuffer.fromString('')); }); + teardown(() => { + disposables.clear(); + }); + suite('hashIdentifier', () => { test('should correctly hash the identifier for untitled scheme URIs', () => { const uri = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); @@ -1296,4 +1304,6 @@ suite('WorkingCopyBackupService', () => { assert.ok(backups.every(backup => backup.typeId === '')); }); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts index 8b53c58a362..9d17566ae2d 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts @@ -16,7 +16,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; @@ -28,7 +28,7 @@ import { INativeHostService } from 'vs/platform/native/common/native'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { createEditorPart, registerTestFileEditor, TestBeforeShutdownEvent, TestEnvironmentService, TestFilesConfigurationService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { createEditorPart, registerTestFileEditor, TestBeforeShutdownEvent, TestEnvironmentService, TestFilesConfigurationService, TestFileService, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -82,8 +82,9 @@ suite('WorkingCopyBackupTracker (native)', function () { override dispose() { super.dispose(); - for (const [_, disposable] of this.pendingBackupOperations) { - disposable.dispose(); + for (const [_, pending] of this.pendingBackupOperations) { + pending.cancel(); + pending.disposable.dispose(); } } @@ -113,11 +114,10 @@ suite('WorkingCopyBackupTracker (native)', function () { let workspaceBackupPath: URI; let accessor: TestServiceAccessor; - let disposables: DisposableStore; + + const disposables = new DisposableStore(); setup(async () => { - disposables = new DisposableStore(); - testDir = URI.file(join(generateUuid(), 'vsctests', 'workingcopybackuptracker')).with({ scheme: Schemas.inMemory }); backupHome = joinPath(testDir, 'Backups'); const workspacesJsonPath = joinPath(backupHome, 'workspaces.json'); @@ -137,8 +137,8 @@ suite('WorkingCopyBackupTracker (native)', function () { return accessor.fileService.writeFile(workspacesJsonPath, VSBuffer.fromString('')); }); - teardown(async () => { - disposables.dispose(); + teardown(() => { + disposables.clear(); }); async function createTracker(autoSaveEnabled = false): Promise<{ accessor: TestServiceAccessor; part: EditorPart; tracker: TestWorkingCopyBackupTracker; instantiationService: IInstantiationService; cleanup: () => Promise }> { @@ -150,19 +150,19 @@ suite('WorkingCopyBackupTracker (native)', function () { } instantiationService.stub(IConfigurationService, configurationService); - instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService, new TestContextService(TestWorkspace), TestEnvironmentService, - new UriIdentityService(new TestFileService()), - new TestFileService() - )); + disposables.add(new UriIdentityService(disposables.add(new TestFileService()))), + disposables.add(new TestFileService()) + ))); const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); accessor = instantiationService.createInstance(TestServiceAccessor); @@ -170,8 +170,9 @@ suite('WorkingCopyBackupTracker (native)', function () { const tracker = instantiationService.createInstance(TestWorkingCopyBackupTracker); const cleanup = async () => { - // File changes could also schedule some backup operations so we need to wait for them before finishing the test - await accessor.workingCopyBackupService.waitForAllBackups(); + await accessor.workingCopyBackupService.waitForAllBackups(); // File changes could also schedule some backup operations so we need to wait for them before finishing the test + + await workbenchTeardown(instantiationService); part.dispose(); tracker.dispose(); @@ -356,7 +357,7 @@ suite('WorkingCopyBackupTracker (native)', function () { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + this._register(accessor.workingCopyService.registerWorkingCopy(this)); } override async backup(token: CancellationToken): Promise { @@ -365,7 +366,7 @@ suite('WorkingCopyBackupTracker (native)', function () { } const resource = toResource.call(this, '/path/custom.txt'); - const customWorkingCopy = new TestBackupWorkingCopy(resource); + const customWorkingCopy = disposables.add(new TestBackupWorkingCopy(resource)); customWorkingCopy.setDirty(true); const event = new TestBeforeShutdownEvent(); @@ -389,7 +390,7 @@ suite('WorkingCopyBackupTracker (native)', function () { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + this._register(accessor.workingCopyService.registerWorkingCopy(this)); } override capabilities = WorkingCopyCapabilities.Untitled | WorkingCopyCapabilities.Scratchpad; @@ -408,7 +409,7 @@ suite('WorkingCopyBackupTracker (native)', function () { } const resource = toResource.call(this, '/path/custom.txt'); - new TestBackupWorkingCopy(resource); + disposables.add(new TestBackupWorkingCopy(resource)); const event = new TestBeforeShutdownEvent(); event.reason = ShutdownReason.QUIT; @@ -716,7 +717,7 @@ suite('WorkingCopyBackupTracker (native)', function () { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + this._register(accessor.workingCopyService.registerWorkingCopy(this)); } override capabilities = WorkingCopyCapabilities.Untitled | WorkingCopyCapabilities.Scratchpad; @@ -747,7 +748,7 @@ suite('WorkingCopyBackupTracker (native)', function () { accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); const resource = toResource.call(this, '/path/custom.txt'); - new TestBackupWorkingCopy(resource); + disposables.add(new TestBackupWorkingCopy(resource)); const event = new TestBeforeShutdownEvent(); event.reason = shutdownReason; @@ -761,4 +762,6 @@ suite('WorkingCopyBackupTracker (native)', function () { await cleanup(); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts index 9d9094116bc..ff4f48a4af9 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts @@ -23,6 +23,8 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'vs/base/common/path'; import { VSBuffer } from 'vs/base/common/buffer'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryService { @@ -30,24 +32,20 @@ export class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryServi readonly _configurationService: TestConfigurationService; readonly _lifecycleService: TestLifecycleService; - constructor(fileService?: IFileService) { + constructor(disposables: DisposableStore, fileService?: IFileService) { const environmentService = TestEnvironmentService; const logService = new NullLogService(); if (!fileService) { - fileService = new FileService(logService); - fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); - fileService.registerProvider(Schemas.vscodeUserData, new InMemoryFileSystemProvider()); + fileService = disposables.add(new FileService(logService)); + disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider()))); + disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new InMemoryFileSystemProvider()))); } const remoteAgentService = new TestRemoteAgentService(); - - const uriIdentityService = new UriIdentityService(fileService); - - const labelService = new LabelService(environmentService, new TestContextService(), new TestPathService(), new TestRemoteAgentService(), new TestStorageService(), new TestLifecycleService()); - - const lifecycleService = new TestLifecycleService(); - + const uriIdentityService = disposables.add(new UriIdentityService(fileService)); + const lifecycleService = disposables.add(new TestLifecycleService()); + const labelService = disposables.add(new LabelService(environmentService, new TestContextService(), new TestPathService(), new TestRemoteAgentService(), disposables.add(new TestStorageService()), lifecycleService)); const configurationService = new TestConfigurationService(); super(fileService, remoteAgentService, environmentService, uriIdentityService, labelService, lifecycleService, logService, configurationService); @@ -60,6 +58,8 @@ export class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryServi suite('WorkingCopyHistoryService', () => { + const disposables = new DisposableStore(); + let testDir: URI; let historyHome: URI; let workHome: URI; @@ -84,7 +84,7 @@ suite('WorkingCopyHistoryService', () => { historyHome = joinPath(testDir, 'User', 'History'); workHome = joinPath(testDir, 'work'); - service = new TestWorkingCopyHistoryService(); + service = disposables.add(new TestWorkingCopyHistoryService(disposables)); fileService = service._fileService; await fileService.createFolder(historyHome); @@ -118,15 +118,15 @@ suite('WorkingCopyHistoryService', () => { } teardown(() => { - service.dispose(); + disposables.clear(); }); test('addEntry', async () => { const addEvents: IWorkingCopyHistoryEvent[] = []; - service.onDidAddEntry(e => addEvents.push(e)); + disposables.add(service.onDidAddEntry(e => addEvents.push(e))); - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); // Add Entry works @@ -164,7 +164,7 @@ suite('WorkingCopyHistoryService', () => { // Invalid working copies are ignored - const workingCopy3 = new TestWorkingCopy(testFile2Path.with({ scheme: 'unsupported' })); + const workingCopy3 = disposables.add(new TestWorkingCopy(testFile2Path.with({ scheme: 'unsupported' }))); const entry3A = await addEntry({ resource: workingCopy3.resource }, CancellationToken.None, false); assert.ok(!entry3A); @@ -173,9 +173,9 @@ suite('WorkingCopyHistoryService', () => { test('renameEntry', async () => { const changeEvents: IWorkingCopyHistoryEvent[] = []; - service.onDidChangeEntry(e => changeEvents.push(e)); + disposables.add(service.onDidChangeEntry(e => changeEvents.push(e))); - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -200,7 +200,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 3); @@ -209,9 +209,9 @@ suite('WorkingCopyHistoryService', () => { test('removeEntry', async () => { const removeEvents: IWorkingCopyHistoryEvent[] = []; - service.onDidRemoveEntry(e => removeEvents.push(e)); + disposables.add(service.onDidRemoveEntry(e => removeEvents.push(e))); - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -242,14 +242,14 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 3); }); test('removeEntry - deletes history entries folder when last entry removed', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); let entry = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -261,7 +261,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); assert.strictEqual((await fileService.exists(dirname(entry.location))), true); @@ -278,17 +278,17 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); assert.strictEqual((await fileService.exists(dirname(entry.location))), false); }); test('removeAll', async () => { let removed = false; - service.onDidRemoveEntries(() => removed = true); + disposables.add(service.onDidRemoveEntries(() => removed = true)); - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -317,7 +317,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 0); @@ -326,8 +326,8 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - simple', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); let entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 0); @@ -355,8 +355,8 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - metadata preserved when stored', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const entry1 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy2.resource }, CancellationToken.None); @@ -370,7 +370,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); let entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 1); @@ -383,7 +383,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - corrupt meta.json is no problem', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -395,7 +395,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); const metaFile = joinPath(dirname(entry1.location), 'entries.json'); assert.ok((await fileService.exists(metaFile))); @@ -407,7 +407,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - missing entries from meta.json is no problem', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -420,7 +420,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); await fileService.del(entry1.location); @@ -430,7 +430,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - in-memory and on-disk entries are merged', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource, source: 'other-source' }, CancellationToken.None); @@ -443,7 +443,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); const entry3 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry4 = await addEntry({ resource: workingCopy1.resource, source: 'other-source' }, CancellationToken.None); @@ -457,7 +457,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - configured max entries respected', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -483,8 +483,8 @@ suite('WorkingCopyHistoryService', () => { }); test('getAll', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); let resources = await service.getAll(CancellationToken.None); assert.strictEqual(resources.length, 0); @@ -510,9 +510,9 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); - const workingCopy3 = new TestWorkingCopy(testFile3Path); + const workingCopy3 = disposables.add(new TestWorkingCopy(testFile3Path)); await addEntry({ resource: workingCopy3.resource, source: 'test-source' }, CancellationToken.None); resources = await service.getAll(CancellationToken.None); @@ -525,7 +525,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getAll - ignores resource when no entries exist', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); @@ -545,7 +545,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); resources = await service.getAll(CancellationToken.None); assert.strictEqual(resources.length, 0); @@ -563,7 +563,7 @@ suite('WorkingCopyHistoryService', () => { } test('entries cleaned up on shutdown', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource, source: 'other-source' }, CancellationToken.None); @@ -585,7 +585,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); let entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 2); @@ -608,7 +608,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 3); @@ -619,9 +619,9 @@ suite('WorkingCopyHistoryService', () => { test('entries are merged when source is same', async () => { let replaced: IWorkingCopyHistoryEntry | undefined = undefined; - service.onDidReplaceEntry(e => replaced = e.entry); + disposables.add(service.onDidReplaceEntry(e => replaced = e.entry)); - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); service._configurationService.setUserConfiguration('workbench.localHistory.mergeWindow', 1); @@ -648,7 +648,7 @@ suite('WorkingCopyHistoryService', () => { }); test('move entries (file rename)', async () => { - const workingCopy = new TestWorkingCopy(testFile1Path); + const workingCopy = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); @@ -695,8 +695,8 @@ suite('WorkingCopyHistoryService', () => { }); test('entries moved (folder rename)', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const entry1A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); @@ -782,4 +782,6 @@ suite('WorkingCopyHistoryService', () => { } } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts index 5bbe9b30267..ef04d7041db 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts @@ -5,14 +5,14 @@ import * as assert from 'assert'; import { Event } from 'vs/base/common/event'; -import { TestContextService, TestStorageService, TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; import { randomPath } from 'vs/base/common/extpath'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { WorkingCopyHistoryTracker } from 'vs/workbench/services/workingCopy/common/workingCopyHistoryTracker'; import { WorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; -import { TestEnvironmentService, TestFileService, TestLifecycleService, TestPathService, TestRemoteAgentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestFileService, TestPathService } from 'vs/workbench/test/browser/workbenchTestServices'; import { DeferredPromise } from 'vs/base/common/async'; import { IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; @@ -25,43 +25,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IWorkingCopyHistoryEntry, IWorkingCopyHistoryEntryDescriptor } from 'vs/workbench/services/workingCopy/common/workingCopyHistory'; import { assertIsDefined } from 'vs/base/common/types'; import { VSBuffer } from 'vs/base/common/buffer'; -import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { NativeWorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/common/workingCopyHistoryService'; -import { NullLogService } from 'vs/platform/log/common/log'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { LabelService } from 'vs/workbench/services/label/common/labelService'; - -class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryService { - - readonly _fileService: IFileService; - readonly _configurationService: TestConfigurationService; - readonly _lifecycleService: TestLifecycleService; - - constructor(testDir: URI | string) { - const environmentService = TestEnvironmentService; - const logService = new NullLogService(); - const fileService = new FileService(logService); - - fileService.registerProvider(Schemas.vscodeUserData, new InMemoryFileSystemProvider()); - - const remoteAgentService = new TestRemoteAgentService(); - - const uriIdentityService = new UriIdentityService(fileService); - - const labelService = new LabelService(environmentService, new TestContextService(), new TestPathService(), new TestRemoteAgentService(), new TestStorageService(), new TestLifecycleService()); - - const lifecycleService = new TestLifecycleService(); - - const configurationService = new TestConfigurationService(); - - super(fileService, remoteAgentService, environmentService, uriIdentityService, labelService, lifecycleService, logService, configurationService); - - this._fileService = fileService; - this._configurationService = configurationService; - this._lifecycleService = lifecycleService; - } -} +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { TestWorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test'; suite('WorkingCopyHistoryTracker', () => { @@ -73,13 +39,14 @@ suite('WorkingCopyHistoryTracker', () => { let workingCopyService: WorkingCopyService; let fileService: IFileService; let configurationService: TestConfigurationService; - let inMemoryFileSystemDisposable: IDisposable; let tracker: WorkingCopyHistoryTracker; let testFile1Path: URI; let testFile2Path: URI; + const disposables = new DisposableStore(); + const testFile1PathContents = 'Hello Foo'; const testFile2PathContents = [ 'Lorem ipsum ', @@ -104,14 +71,12 @@ suite('WorkingCopyHistoryTracker', () => { historyHome = joinPath(testDir, 'User', 'History'); workHome = joinPath(testDir, 'work'); - workingCopyHistoryService = new TestWorkingCopyHistoryService(testDir); - workingCopyService = new WorkingCopyService(); + workingCopyHistoryService = disposables.add(new TestWorkingCopyHistoryService(disposables)); + workingCopyService = disposables.add(new WorkingCopyService()); fileService = workingCopyHistoryService._fileService; configurationService = workingCopyHistoryService._configurationService; - inMemoryFileSystemDisposable = fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); - - tracker = createTracker(); + tracker = disposables.add(createTracker()); await fileService.createFolder(historyHome); await fileService.createFolder(workHome); @@ -127,7 +92,7 @@ suite('WorkingCopyHistoryTracker', () => { return new WorkingCopyHistoryTracker( workingCopyService, workingCopyHistoryService, - new UriIdentityService(new TestFileService()), + disposables.add(new UriIdentityService(disposables.add(new TestFileService()))), new TestPathService(undefined, Schemas.file), configurationService, new UndoRedoService(new TestDialogService(), new TestNotificationService()), @@ -137,28 +102,23 @@ suite('WorkingCopyHistoryTracker', () => { } teardown(async () => { - workingCopyHistoryService.dispose(); - workingCopyService.dispose(); - tracker.dispose(); - await fileService.del(testDir, { recursive: true }); - - inMemoryFileSystemDisposable.dispose(); + disposables.clear(); }); test('history entry added on save', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const stat1 = await fileService.resolve(workingCopy1.resource, { resolveMetadata: true }); const stat2 = await fileService.resolve(workingCopy2.resource, { resolveMetadata: true }); - workingCopyService.registerWorkingCopy(workingCopy1); - workingCopyService.registerWorkingCopy(workingCopy2); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy1)); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy2)); const saveResult = new DeferredPromise(); let addedCounter = 0; - workingCopyHistoryService.onDidAddEntry(e => { + disposables.add(workingCopyHistoryService.onDidAddEntry(e => { if (isEqual(e.entry.workingCopy.resource, workingCopy1.resource) || isEqual(e.entry.workingCopy.resource, workingCopy2.resource)) { addedCounter++; @@ -166,7 +126,7 @@ suite('WorkingCopyHistoryTracker', () => { saveResult.complete(); } } - }); + })); await workingCopy1.save(undefined, stat1); await workingCopy2.save(undefined, stat2); @@ -185,7 +145,7 @@ suite('WorkingCopyHistoryTracker', () => { // Recreate to apply settings tracker.dispose(); - tracker = createTracker(); + tracker = disposables.add(createTracker()); return assertNoLocalHistoryEntryAddedWithSettingsConfigured(); }); @@ -197,17 +157,17 @@ suite('WorkingCopyHistoryTracker', () => { }); async function assertNoLocalHistoryEntryAddedWithSettingsConfigured(): Promise { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const stat1 = await fileService.resolve(workingCopy1.resource, { resolveMetadata: true }); const stat2 = await fileService.resolve(workingCopy2.resource, { resolveMetadata: true }); - workingCopyService.registerWorkingCopy(workingCopy1); - workingCopyService.registerWorkingCopy(workingCopy2); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy1)); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy2)); const saveResult = new DeferredPromise(); - workingCopyHistoryService.onDidAddEntry(e => { + disposables.add(workingCopyHistoryService.onDidAddEntry(e => { if (isEqual(e.entry.workingCopy.resource, workingCopy1.resource)) { assert.fail('Unexpected working copy history entry: ' + e.entry.workingCopy.resource.toString()); } @@ -215,7 +175,7 @@ suite('WorkingCopyHistoryTracker', () => { if (isEqual(e.entry.workingCopy.resource, workingCopy2.resource)) { saveResult.complete(); } - }); + })); await workingCopy1.save(undefined, stat1); await workingCopy2.save(undefined, stat2); @@ -226,7 +186,7 @@ suite('WorkingCopyHistoryTracker', () => { test('entries moved (file rename)', async () => { const entriesMoved = Event.toPromise(workingCopyHistoryService.onDidMoveEntries); - const workingCopy = new TestWorkingCopy(testFile1Path); + const workingCopy = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); @@ -272,8 +232,8 @@ suite('WorkingCopyHistoryTracker', () => { test('entries moved (folder rename)', async () => { const entriesMoved = Event.toPromise(workingCopyHistoryService.onDidMoveEntries); - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const entry1A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); @@ -352,5 +312,6 @@ suite('WorkingCopyHistoryTracker', () => { } } }); -}); + ensureNoDisposablesAreLeakedInTestSuite(); +}); diff --git a/src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts b/src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts deleted file mode 100644 index 636b664aa6f..00000000000 --- a/src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts +++ /dev/null @@ -1,144 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Emitter } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo, WorkspaceTrustRequestOptions, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; - - -export class TestWorkspaceTrustEnablementService implements IWorkspaceTrustEnablementService { - _serviceBrand: undefined; - - constructor(private isEnabled: boolean = true) { } - - isWorkspaceTrustEnabled(): boolean { - return this.isEnabled; - } -} - -export class TestWorkspaceTrustManagementService implements IWorkspaceTrustManagementService { - _serviceBrand: undefined; - - private _onDidChangeTrust = new Emitter(); - onDidChangeTrust = this._onDidChangeTrust.event; - - private _onDidChangeTrustedFolders = new Emitter(); - onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event; - - private _onDidInitiateWorkspaceTrustRequestOnStartup = new Emitter(); - onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; - - - constructor( - private trusted: boolean = true - ) { } - - get acceptsOutOfWorkspaceFiles(): boolean { - throw new Error('Method not implemented.'); - } - - set acceptsOutOfWorkspaceFiles(value: boolean) { - throw new Error('Method not implemented.'); - } - - addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable { - throw new Error('Method not implemented.'); - } - - getTrustedUris(): URI[] { - throw new Error('Method not implemented.'); - } - - setParentFolderTrust(trusted: boolean): Promise { - throw new Error('Method not implemented.'); - } - - getUriTrustInfo(uri: URI): Promise { - throw new Error('Method not implemented.'); - } - - async setTrustedUris(folders: URI[]): Promise { - throw new Error('Method not implemented.'); - } - - async setUrisTrust(uris: URI[], trusted: boolean): Promise { - throw new Error('Method not implemented.'); - } - - canSetParentFolderTrust(): boolean { - throw new Error('Method not implemented.'); - } - - canSetWorkspaceTrust(): boolean { - throw new Error('Method not implemented.'); - } - - isWorkspaceTrusted(): boolean { - return this.trusted; - } - - isWorkspaceTrustForced(): boolean { - return false; - } - - get workspaceTrustInitialized(): Promise { - return Promise.resolve(); - } - - get workspaceResolved(): Promise { - return Promise.resolve(); - } - - async setWorkspaceTrust(trusted: boolean): Promise { - if (this.trusted !== trusted) { - this.trusted = trusted; - this._onDidChangeTrust.fire(this.trusted); - } - } -} - -export class TestWorkspaceTrustRequestService implements IWorkspaceTrustRequestService { - _serviceBrand: any; - - private readonly _onDidInitiateOpenFilesTrustRequest = new Emitter(); - readonly onDidInitiateOpenFilesTrustRequest = this._onDidInitiateOpenFilesTrustRequest.event; - - private readonly _onDidInitiateWorkspaceTrustRequest = new Emitter(); - readonly onDidInitiateWorkspaceTrustRequest = this._onDidInitiateWorkspaceTrustRequest.event; - - private readonly _onDidInitiateWorkspaceTrustRequestOnStartup = new Emitter(); - readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; - - constructor(private readonly _trusted: boolean) { } - - requestOpenUrisHandler = async (uris: URI[]) => { - return WorkspaceTrustUriResponse.Open; - }; - - requestOpenFilesTrust(uris: URI[]): Promise { - return this.requestOpenUrisHandler(uris); - } - - async completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse: boolean): Promise { - throw new Error('Method not implemented.'); - } - - cancelWorkspaceTrustRequest(): void { - throw new Error('Method not implemented.'); - } - - async completeWorkspaceTrustRequest(trusted?: boolean): Promise { - throw new Error('Method not implemented.'); - } - - async requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise { - return this._trusted; - } - - requestWorkspaceTrustOnStartup(): void { - throw new Error('Method not implemented.'); - } -} diff --git a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts index 037330ae50e..a39caa0061e 100644 --- a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts +++ b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts @@ -21,8 +21,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { WorkspaceTrustEnablementService, WorkspaceTrustManagementService, WORKSPACE_TRUST_STORAGE_KEY } from 'vs/workbench/services/workspaces/common/workspaceTrust'; -import { TestWorkspaceTrustEnablementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; -import { TestContextService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestStorageService, TestWorkspaceTrustEnablementService } from 'vs/workbench/test/common/workbenchTestServices'; suite('Workspace Trust', () => { let instantiationService: TestInstantiationService; diff --git a/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts index fbc271d79e1..d94e0412166 100644 --- a/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts @@ -36,14 +36,10 @@ suite('Diff editor input', () => { } } - let disposables: DisposableStore; - - setup(() => { - disposables = new DisposableStore(); - }); + const disposables = new DisposableStore(); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', () => { diff --git a/src/vs/workbench/test/browser/parts/editor/editor.test.ts b/src/vs/workbench/test/browser/parts/editor/editor.test.ts index a49e5dd00c1..ced0b141475 100644 --- a/src/vs/workbench/test/browser/parts/editor/editor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editor.test.ts @@ -12,7 +12,7 @@ import { workbenchInstantiationService, TestServiceAccessor, TestEditorInput, re import { Schemas } from 'vs/base/common/network'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { whenEditorClosed } from 'vs/workbench/browser/editor'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -39,22 +39,11 @@ suite('Workbench editor utils', () => { let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; - async function createServices(): Promise { - const instantiationService = workbenchInstantiationService(undefined, disposables); - - const part = await createEditorPart(instantiationService, disposables); - instantiationService.stub(IEditorGroupsService, part); - - const editorService = instantiationService.createInstance(EditorService); - instantiationService.stub(IEditorService, editorService); - - return instantiationService.createInstance(TestServiceAccessor); - } - setup(() => { instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.untitledTextEditorService); disposables.add(registerTestFileEditor()); disposables.add(registerTestSideBySideEditor()); disposables.add(registerTestResourceEditor()); @@ -62,8 +51,6 @@ suite('Workbench editor utils', () => { }); teardown(() => { - accessor.untitledTextEditorService.dispose(); - disposables.clear(); }); @@ -99,8 +86,8 @@ suite('Workbench editor utils', () => { }); test('EditorInputCapabilities', () => { - const testInput1 = new TestFileEditorInput(URI.file('resource1'), 'testTypeId'); - const testInput2 = new TestFileEditorInput(URI.file('resource2'), 'testTypeId'); + const testInput1 = disposables.add(new TestFileEditorInput(URI.file('resource1'), 'testTypeId')); + const testInput2 = disposables.add(new TestFileEditorInput(URI.file('resource2'), 'testTypeId')); testInput1.capabilities = EditorInputCapabilities.None; assert.strictEqual(testInput1.hasCapability(EditorInputCapabilities.None), true); @@ -162,7 +149,7 @@ suite('Workbench editor utils', () => { assert.ok(!EditorResourceAccessor.getCanonicalUri(null!)); assert.ok(!EditorResourceAccessor.getOriginalUri(null!)); - const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const untitled = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(untitled)?.toString(), untitled.resource.toString()); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(untitled, { supportSideBySide: SideBySideEditor.PRIMARY })?.toString(), untitled.resource.toString()); @@ -182,7 +169,7 @@ suite('Workbench editor utils', () => { assert.strictEqual(EditorResourceAccessor.getOriginalUri(untitled, { filterByScheme: [Schemas.file, Schemas.untitled] })?.toString(), untitled.resource.toString()); assert.ok(!EditorResourceAccessor.getOriginalUri(untitled, { filterByScheme: Schemas.file })); - const file = new TestEditorInput(URI.file('/some/path.txt'), 'editorResourceFileTest'); + const file = disposables.add(new TestEditorInput(URI.file('/some/path.txt'), 'editorResourceFileTest')); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(file)?.toString(), file.resource.toString()); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(file, { supportSideBySide: SideBySideEditor.PRIMARY })?.toString(), file.resource.toString()); @@ -246,7 +233,7 @@ suite('Workbench editor utils', () => { const resource = URI.file('/some/path.txt'); const preferredResource = URI.file('/some/PATH.txt'); - const fileWithPreferredResource = new TestEditorInputWithPreferredResource(URI.file('/some/path.txt'), URI.file('/some/PATH.txt'), 'editorResourceFileTest'); + const fileWithPreferredResource = disposables.add(new TestEditorInputWithPreferredResource(URI.file('/some/path.txt'), URI.file('/some/PATH.txt'), 'editorResourceFileTest')); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(fileWithPreferredResource)?.toString(), resource.toString()); assert.strictEqual(EditorResourceAccessor.getOriginalUri(fileWithPreferredResource)?.toString(), preferredResource.toString()); @@ -363,13 +350,13 @@ suite('Workbench editor utils', () => { assert.strictEqual(isEditorIdentifier(undefined), false); assert.strictEqual(isEditorIdentifier('undefined'), false); - const testInput1 = new TestFileEditorInput(URI.file('resource1'), 'testTypeId'); + const testInput1 = disposables.add(new TestFileEditorInput(URI.file('resource1'), 'testTypeId')); assert.strictEqual(isEditorIdentifier(testInput1), false); assert.strictEqual(isEditorIdentifier({ editor: testInput1, groupId: 3 }), true); }); test('isEditorInputWithOptionsAndGroup', () => { - const editorInput = new TestFileEditorInput(URI.file('resource1'), 'testTypeId'); + const editorInput = disposables.add(new TestFileEditorInput(URI.file('resource1'), 'testTypeId')); assert.strictEqual(isEditorInput(editorInput), true); assert.strictEqual(isEditorInputWithOptions(editorInput), false); assert.strictEqual(isEditorInputWithOptionsAndGroup(editorInput), false); @@ -434,6 +421,18 @@ suite('Workbench editor utils', () => { return testWhenEditorClosed(false, true, toResource.call(this, '/path/index.txt'), toResource.call(this, '/test.html')); }); + async function createServices(): Promise { + const instantiationService = workbenchInstantiationService(undefined, disposables); + + const part = await createEditorPart(instantiationService, disposables); + instantiationService.stub(IEditorGroupsService, part); + + const editorService = disposables.add(instantiationService.createInstance(EditorService)); + instantiationService.stub(IEditorService, editorService); + + return instantiationService.createInstance(TestServiceAccessor); + } + async function testWhenEditorClosed(sideBySide: boolean, custom: boolean, ...resources: URI[]): Promise { const accessor = await createServices(); @@ -453,4 +452,6 @@ suite('Workbench editor utils', () => { await closedPromise; } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts index 6187bda979f..474fe6c56bb 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts @@ -15,18 +15,17 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('TextDiffEditorModel', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts index 9d1a741e97c..3fe93c47cf6 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts @@ -22,7 +22,7 @@ suite('EditorInput', () => { let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; - let disposables: DisposableStore; + const disposables = new DisposableStore(); const testResource: URI = URI.from({ scheme: 'random', path: '/path' }); const untypedResourceEditorInput: IResourceEditorInput = { resource: testResource, options: { override: DEFAULT_EDITOR_ASSOCIATION.id } }; @@ -52,7 +52,6 @@ suite('EditorInput', () => { }; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); @@ -74,7 +73,7 @@ suite('EditorInput', () => { }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); class MyEditorInput extends EditorInput { diff --git a/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts b/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts index 8baa06d4b3d..380a2ad5ad0 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts @@ -19,16 +19,16 @@ import { URI } from 'vs/base/common/uri'; import { EditorPaneDescriptor, EditorPaneRegistry } from 'vs/workbench/browser/editor'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorModel } from 'vs/platform/editor/common/editor'; -import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; -import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { TestStorageService, TestWorkspaceTrustManagementService } from 'vs/workbench/test/common/workbenchTestServices'; import { extUri } from 'vs/base/common/resources'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const NullThemeService = new TestThemeService(); @@ -37,8 +37,11 @@ const editorInputRegistry: IEditorFactoryRegistry = Registry.as(EditorExtensions class TestEditor extends EditorPane { - constructor(@ITelemetryService telemetryService: ITelemetryService) { - super('TestEditor', NullTelemetryService, NullThemeService, new TestStorageService()); + constructor() { + const disposables = new DisposableStore(); + super('TestEditor', NullTelemetryService, NullThemeService, disposables.add(new TestStorageService())); + + this._register(disposables); } override getId(): string { return 'testEditor'; } @@ -46,10 +49,13 @@ class TestEditor extends EditorPane { protected createEditor(): any { } } -export class OtherTestEditor extends EditorPane { +class OtherTestEditor extends EditorPane { - constructor(@ITelemetryService telemetryService: ITelemetryService) { - super('testOtherEditor', NullTelemetryService, NullThemeService, new TestStorageService()); + constructor() { + const disposables = new DisposableStore(); + super('testOtherEditor', NullTelemetryService, NullThemeService, disposables.add(new TestStorageService())); + + this._register(disposables); } override getId(): string { return 'testOtherEditor'; } @@ -106,9 +112,15 @@ class TestResourceEditorInput extends TextResourceEditorInput { } suite('EditorPane', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('EditorPane API', async () => { - const editor = new TestEditor(NullTelemetryService); - const input = new OtherTestInput(); + const editor = new TestEditor(); + const input = disposables.add(new OtherTestInput()); const options = {}; assert(!editor.isVisible()); @@ -120,9 +132,6 @@ suite('EditorPane', () => { editor.setVisible(true, group); assert(editor.isVisible()); assert.strictEqual(editor.group, group); - input.onWillDispose(() => { - assert(false); - }); editor.dispose(); editor.clearInput(); editor.setVisible(false, group); @@ -144,57 +153,46 @@ suite('EditorPane', () => { const oldEditorsCnt = editorRegistry.getEditorPanes().length; const oldInputCnt = editorRegistry.getEditors().length; - const dispose1 = editorRegistry.registerEditorPane(editorDescriptor1, [new SyncDescriptor(TestInput)]); - const dispose2 = editorRegistry.registerEditorPane(editorDescriptor2, [new SyncDescriptor(TestInput), new SyncDescriptor(OtherTestInput)]); + disposables.add(editorRegistry.registerEditorPane(editorDescriptor1, [new SyncDescriptor(TestInput)])); + disposables.add(editorRegistry.registerEditorPane(editorDescriptor2, [new SyncDescriptor(TestInput), new SyncDescriptor(OtherTestInput)])); assert.strictEqual(editorRegistry.getEditorPanes().length, oldEditorsCnt + 2); assert.strictEqual(editorRegistry.getEditors().length, oldInputCnt + 3); - assert.strictEqual(editorRegistry.getEditorPane(new TestInput()), editorDescriptor2); - assert.strictEqual(editorRegistry.getEditorPane(new OtherTestInput()), editorDescriptor2); + assert.strictEqual(editorRegistry.getEditorPane(disposables.add(new TestInput())), editorDescriptor2); + assert.strictEqual(editorRegistry.getEditorPane(disposables.add(new OtherTestInput())), editorDescriptor2); assert.strictEqual(editorRegistry.getEditorPaneByType('id1'), editorDescriptor1); assert.strictEqual(editorRegistry.getEditorPaneByType('id2'), editorDescriptor2); assert(!editorRegistry.getEditorPaneByType('id3')); - - dispose([dispose1, dispose2]); }); test('Editor Pane Lookup favors specific class over superclass (match on specific class)', function () { const d1 = EditorPaneDescriptor.create(TestEditor, 'id1', 'name'); - const disposables = new DisposableStore(); - disposables.add(registerTestResourceEditor()); disposables.add(editorRegistry.registerEditorPane(d1, [new SyncDescriptor(TestResourceEditorInput)])); const inst = workbenchInstantiationService(undefined, disposables); - const editor = editorRegistry.getEditorPane(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined))!.instantiate(inst); + const editor = disposables.add(editorRegistry.getEditorPane(disposables.add(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined)))!.instantiate(inst)); assert.strictEqual(editor.getId(), 'testEditor'); - const otherEditor = editorRegistry.getEditorPane(inst.createInstance(TextResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined))!.instantiate(inst); + const otherEditor = disposables.add(editorRegistry.getEditorPane(disposables.add(inst.createInstance(TextResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined)))!.instantiate(inst)); assert.strictEqual(otherEditor.getId(), 'workbench.editors.textResourceEditor'); - - disposables.dispose(); }); test('Editor Pane Lookup favors specific class over superclass (match on super class)', function () { - const disposables = new DisposableStore(); - const inst = workbenchInstantiationService(undefined, disposables); disposables.add(registerTestResourceEditor()); - const editor = editorRegistry.getEditorPane(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined))!.instantiate(inst); + const editor = disposables.add(editorRegistry.getEditorPane(disposables.add(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined)))!.instantiate(inst)); assert.strictEqual('workbench.editors.textResourceEditor', editor.getId()); - - disposables.dispose(); }); test('Editor Input Serializer', function () { - const disposables = new DisposableStore(); - const testInput = new TestEditorInput(URI.file('/fake'), 'testTypeId'); + const testInput = disposables.add(new TestEditorInput(URI.file('/fake'), 'testTypeId')); workbenchInstantiationService(undefined, disposables).invokeFunction(accessor => editorInputRegistry.start(accessor)); disposables.add(editorInputRegistry.registerEditorSerializer(testInput.typeId, TestInputSerializer)); @@ -206,8 +204,6 @@ suite('EditorPane', () => { // throws when registering serializer for same type assert.throws(() => editorInputRegistry.registerEditorSerializer(testInput.typeId, TestInputSerializer)); - - disposables.dispose(); }); test('EditorMemento - basics', function () { @@ -228,7 +224,7 @@ suite('EditorPane', () => { } const rawMemento = Object.create(null); - let memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + let memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); let res = memento.loadEditorState(testGroup0, URI.file('/A')); assert.ok(!res); @@ -263,7 +259,7 @@ suite('EditorPane', () => { memento.saveState(); - memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); assert.ok(memento.loadEditorState(testGroup0, URI.file('/C'))); assert.ok(memento.loadEditorState(testGroup0, URI.file('/D'))); assert.ok(memento.loadEditorState(testGroup0, URI.file('/E'))); @@ -289,7 +285,7 @@ suite('EditorPane', () => { interface TestViewState { line: number } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); memento.saveEditorState(testGroup0, URI.file('/some/folder/file-1.txt'), { line: 1 }); memento.saveEditorState(testGroup0, URI.file('/some/folder/file-2.txt'), { line: 2 }); @@ -332,9 +328,9 @@ suite('EditorPane', () => { } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService()); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService())); - const testInputA = new TestEditorInput(URI.file('/A')); + const testInputA = disposables.add(new TestEditorInput(URI.file('/A'))); let res = memento.loadEditorState(testGroup0, testInputA); assert.ok(!res); @@ -370,9 +366,9 @@ suite('EditorPane', () => { } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService()); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService())); - const testInputA = new TestEditorInput(URI.file('/A')); + const testInputA = disposables.add(new TestEditorInput(URI.file('/A'))); let res = memento.loadEditorState(testGroup0, testInputA); assert.ok(!res); @@ -388,7 +384,7 @@ suite('EditorPane', () => { res = memento.loadEditorState(testGroup0, testInputA); assert.ok(res); - const testInputB = new TestEditorInput(URI.file('/B')); + const testInputB = disposables.add(new TestEditorInput(URI.file('/B'))); res = memento.loadEditorState(testGroup0, testInputB); assert.ok(!res); @@ -422,7 +418,7 @@ suite('EditorPane', () => { interface TestViewState { line: number } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); const resource = URI.file('/some/folder/file-1.txt'); memento.saveEditorState(testGroup0, resource, { line: 1 }); @@ -459,7 +455,7 @@ suite('EditorPane', () => { class TrustRequiredTestEditor extends EditorPane { constructor(@ITelemetryService telemetryService: ITelemetryService) { - super('TestEditor', NullTelemetryService, NullThemeService, new TestStorageService()); + super('TestEditor', NullTelemetryService, NullThemeService, disposables.add(new TestStorageService())); } override getId(): string { return 'trustRequiredTestEditor'; } @@ -484,17 +480,15 @@ suite('EditorPane', () => { } } - const disposables = new DisposableStore(); - const instantiationService = workbenchInstantiationService(undefined, disposables); - const workspaceTrustService = instantiationService.createInstance(TestWorkspaceTrustManagementService); + const workspaceTrustService = disposables.add(instantiationService.createInstance(TestWorkspaceTrustManagementService)); instantiationService.stub(IWorkspaceTrustManagementService, workspaceTrustService); workspaceTrustService.setWorkspaceTrust(false); const editorPart = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, editorPart); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); const group = editorPart.activeGroup; @@ -502,7 +496,7 @@ suite('EditorPane', () => { const editorDescriptor = EditorPaneDescriptor.create(TrustRequiredTestEditor, 'id1', 'name'); disposables.add(editorRegistry.registerEditorPane(editorDescriptor, [new SyncDescriptor(TrustRequiredTestInput)])); - const testInput = new TrustRequiredTestInput(); + const testInput = disposables.add(new TrustRequiredTestInput()); await group.openEditor(testInput); assert.strictEqual(group.activeEditorPane?.getId(), WorkspaceTrustRequiredPlaceholderEditor.ID); @@ -520,6 +514,8 @@ suite('EditorPane', () => { workspaceTrustService.setWorkspaceTrust(false); assert.strictEqual(await getEditorPaneIdAsync(), WorkspaceTrustRequiredPlaceholderEditor.ID); - dispose(disposables); + await group.closeAllEditors(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts index 2fd994d4639..d1a44ee58f6 100644 --- a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts @@ -16,7 +16,7 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura suite('ResourceEditorInput', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; class TestResourceEditorInput extends AbstractResourceEditorInput { @@ -34,12 +34,11 @@ suite('ResourceEditorInput', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts index 9ace2cbba4c..7eeb6e81a60 100644 --- a/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts @@ -13,14 +13,10 @@ import { TestFileEditorInput, workbenchInstantiationService } from 'vs/workbench suite('SideBySideEditorInput', () => { - let disposables: DisposableStore; - - setup(() => { - disposables = new DisposableStore(); - }); + const disposables = new DisposableStore(); teardown(() => { - disposables.dispose(); + disposables.clear(); }); class MyEditorInput extends EditorInput { diff --git a/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts b/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts index f678591223a..4574694f503 100644 --- a/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { workbenchInstantiationService, TestServiceAccessor, registerTestFileEditor, createEditorPart, TestTextFileEditor } from 'vs/workbench/test/browser/workbenchTestServices'; import { IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -35,7 +35,7 @@ suite('TextEditorPane', () => { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); return instantiationService.createInstance(TestServiceAccessor); @@ -50,16 +50,16 @@ suite('TextEditorPane', () => { assert.ok(pane && isEditorPaneWithSelection(pane)); const onDidFireSelectionEventOfEditType = new DeferredPromise(); - pane.onDidChangeSelection(e => { + disposables.add(pane.onDidChangeSelection(e => { if (e.reason === EditorPaneSelectionChangeReason.EDIT) { onDidFireSelectionEventOfEditType.complete(e); } - }); + })); // Changing model reports selection change // of EDIT kind - const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; + const model = disposables.add(await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel); model.textEditorModel.setValue('Hello World'); const event = await onDidFireSelectionEventOfEditType.p; @@ -83,6 +83,9 @@ suite('TextEditorPane', () => { const newSelection = pane.getSelection(); assert.ok(newSelection); assert.strictEqual(newSelection.compare(selection), EditorPaneSelectionCompareResult.IDENTICAL); + + await model.revert(); + await pane.group?.closeAllEditors(); }); test('TextEditorPaneSelection', function () { @@ -96,4 +99,6 @@ suite('TextEditorPane', () => { assert.strictEqual(sel1.compare(sel3), EditorPaneSelectionCompareResult.DIFFERENT); assert.strictEqual(sel1.compare(sel4), EditorPaneSelectionCompareResult.DIFFERENT); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts index d2c732e94db..797e6c88c2f 100644 --- a/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts @@ -15,18 +15,18 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('TextResourceEditorInput', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); + let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 1340c38c2de..3fa67419252 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -99,7 +99,7 @@ import { IInputBox, IInputOptions, IPickOptions, IQuickInputButton, IQuickInputS import { QuickInputService } from 'vs/workbench/services/quickinput/browser/quickInputService'; import { IListService } from 'vs/platform/list/browser/listService'; import { win32, posix } from 'vs/base/common/path'; -import { TestContextService, TestStorageService, TestTextResourcePropertiesService, TestExtensionService, TestProductService, createFileStat, TestLoggerService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestStorageService, TestTextResourcePropertiesService, TestExtensionService, TestProductService, createFileStat, TestLoggerService, TestWorkspaceTrustManagementService, TestWorkspaceTrustRequestService } from 'vs/workbench/test/common/workbenchTestServices'; import { IViewsService, IView, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -121,7 +121,6 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { IEnterWorkspaceResult, IRecent, IRecentlyOpened, IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustManagementService, TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IExtensionTerminalProfile, IShellLaunchConfig, ITerminalBackend, ITerminalProfile, TerminalIcon, TerminalLocation, TerminalShellType } from 'vs/platform/terminal/common/terminal'; import { ICreateTerminalOptions, IDeserializedTerminalEditorInput, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; import { assertIsDefined } from 'vs/base/common/types'; @@ -188,14 +187,14 @@ Registry.as(EditorExtensions.EditorFactory).registerFile export class TestTextResourceEditor extends TextResourceEditor { protected override createEditorControl(parent: HTMLElement, configuration: any): void { - this.editorControl = this.instantiationService.createInstance(TestCodeEditor, parent, configuration, {}); + this.editorControl = this._register(this.instantiationService.createInstance(TestCodeEditor, parent, configuration, {})); } } export class TestTextFileEditor extends TextFileEditor { protected override createEditorControl(parent: HTMLElement, configuration: any): void { - this.editorControl = this.instantiationService.createInstance(TestCodeEditor, parent, configuration, { contributions: [] }); + this.editorControl = this._register(this.instantiationService.createInstance(TestCodeEditor, parent, configuration, { contributions: [] })); } setSelection(selection: Selection | undefined, reason: EditorPaneSelectionChangeReason): void { @@ -243,7 +242,7 @@ export function workbenchInstantiationService( }, disposables: Pick = new DisposableStore() ): TestInstantiationService { - const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()]))); + const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, disposables.add(new TestLifecycleService())]))); instantiationService.stub(IEditorWorkerService, new TestEditorWorkerService()); instantiationService.stub(IWorkingCopyService, disposables.add(new TestWorkingCopyService())); @@ -285,15 +284,15 @@ export function workbenchInstantiationService( instantiationService.stub(IThemeService, themeService); instantiationService.stub(ILanguageConfigurationService, disposables.add(new TestLanguageConfigurationService())); instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); - const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); + const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : disposables.add(new TestFileService()); instantiationService.stub(IFileService, fileService); const uriIdentityService = new UriIdentityService(fileService); disposables.add(uriIdentityService); instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService(contextKeyService, configService, workspaceContextService, environmentService, uriIdentityService, fileService))); - instantiationService.stub(IUriIdentityService, uriIdentityService); + instantiationService.stub(IUriIdentityService, disposables.add(uriIdentityService)); const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, disposables.add(new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService()))); instantiationService.stub(IUserDataProfileService, disposables.add(new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService))); - instantiationService.stub(IWorkingCopyBackupService, overrides?.workingCopyBackupService ? overrides?.workingCopyBackupService(instantiationService) : new TestWorkingCopyBackupService()); + instantiationService.stub(IWorkingCopyBackupService, overrides?.workingCopyBackupService ? overrides?.workingCopyBackupService(instantiationService) : disposables.add(new TestWorkingCopyBackupService())); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(INotificationService, new TestNotificationService()); instantiationService.stub(IUntitledTextEditorService, disposables.add(instantiationService.createInstance(UntitledTextEditorService))); @@ -323,7 +322,8 @@ export function workbenchInstantiationService( const hoverService = instantiationService.stub(IHoverService, instantiationService.createInstance(TestHoverService)); instantiationService.stub(IQuickInputService, disposables.add(new QuickInputService(configService, instantiationService, keybindingService, contextKeyService, themeService, layoutService, hoverService))); instantiationService.stub(IWorkspacesService, new TestWorkspacesService()); - instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService()); + instantiationService.stub(IWorkspaceTrustManagementService, disposables.add(new TestWorkspaceTrustManagementService())); + instantiationService.stub(IWorkspaceTrustRequestService, disposables.add(new TestWorkspaceTrustRequestService(false))); instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService()); instantiationService.stub(IElevatedFileService, new BrowserElevatedFileService()); instantiationService.stub(IRemoteSocketFactoryService, new RemoteSocketFactoryService()); @@ -1195,17 +1195,20 @@ export class InMemoryTestWorkingCopyBackupService extends BrowserWorkingCopyBack discardedBackups: IWorkingCopyIdentifier[]; constructor() { + const disposables = new DisposableStore(); const environmentService = TestEnvironmentService; const logService = new NullLogService(); - const fileService = new FileService(logService); - fileService.registerProvider(Schemas.file, new InMemoryFileSystemProvider()); - fileService.registerProvider(Schemas.vscodeUserData, new InMemoryFileSystemProvider()); + const fileService = disposables.add(new FileService(logService)); + disposables.add(fileService.registerProvider(Schemas.file, disposables.add(new InMemoryFileSystemProvider()))); + disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new InMemoryFileSystemProvider()))); super(new TestContextService(TestWorkspace), environmentService, fileService, logService); this.backupResourceJoiners = []; this.discardBackupJoiners = []; this.discardedBackups = []; + + this._register(disposables); } testGetFileService(): IFileService { @@ -1246,26 +1249,26 @@ export class InMemoryTestWorkingCopyBackupService extends BrowserWorkingCopyBack } } -export class TestLifecycleService implements ILifecycleService { +export class TestLifecycleService extends Disposable implements ILifecycleService { declare readonly _serviceBrand: undefined; phase!: LifecyclePhase; startupKind!: StartupKind; - private readonly _onBeforeShutdown = new Emitter(); + private readonly _onBeforeShutdown = this._register(new Emitter()); get onBeforeShutdown(): Event { return this._onBeforeShutdown.event; } - private readonly _onBeforeShutdownError = new Emitter(); + private readonly _onBeforeShutdownError = this._register(new Emitter()); get onBeforeShutdownError(): Event { return this._onBeforeShutdownError.event; } - private readonly _onShutdownVeto = new Emitter(); + private readonly _onShutdownVeto = this._register(new Emitter()); get onShutdownVeto(): Event { return this._onShutdownVeto.event; } - private readonly _onWillShutdown = new Emitter(); + private readonly _onWillShutdown = this._register(new Emitter()); get onWillShutdown(): Event { return this._onWillShutdown.event; } - private readonly _onDidShutdown = new Emitter(); + private readonly _onDidShutdown = this._register(new Emitter()); get onDidShutdown(): Event { return this._onDidShutdown.event; } async when(): Promise { } @@ -1488,12 +1491,14 @@ export class TestEditorInput extends EditorInput { } export function registerTestEditor(id: string, inputs: SyncDescriptor[], serializerInputId?: string): IDisposable { + const disposables = new DisposableStore(); + class TestEditor extends EditorPane { private _scopedContextKeyService: IContextKeyService; constructor() { - super(id, NullTelemetryService, new TestThemeService(), new TestStorageService()); + super(id, NullTelemetryService, new TestThemeService(), disposables.add(new TestStorageService())); this._scopedContextKeyService = new MockContextKeyService(); } @@ -1512,8 +1517,6 @@ export function registerTestEditor(id: string, inputs: SyncDescriptor(Extensions.EditorPane).registerEditorPane(EditorPaneDescriptor.create(TestEditor, id, 'Test Editor Control'), inputs)); if (serializerInputId) { @@ -2089,3 +2092,22 @@ export class TestWebExtensionsScannerService implements IWebExtensionsScannerSer throw new Error('Method not implemented.'); } } + +export async function workbenchTeardown(instantiationService: IInstantiationService): Promise { + return instantiationService.invokeFunction(async accessor => { + const workingCopyService = accessor.get(IWorkingCopyService); + const editorGroupService = accessor.get(IEditorGroupsService); + + for (const workingCopy of workingCopyService.workingCopies) { + await workingCopy.revert(); + } + + for (const group of editorGroupService.groups) { + await group.closeAllEditors(); + } + + for (const group of editorGroupService.groups) { + editorGroupService.removeGroup(group); + } + }); +} diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 627f0ea0b77..117a4412509 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -28,6 +28,7 @@ import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { AutoSaveMode, IAutoSaveConfiguration, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo, WorkspaceTrustRequestOptions, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; export class TestLoggerService extends AbstractLoggerService { constructor(logsHome?: URI) { @@ -315,3 +316,141 @@ export const NullFilesConfigurationService = new class implements IFilesConfigur async updateReadonly(resource: URI, readonly: boolean | 'toggle' | 'reset'): Promise { } preventSaveConflicts(resource: URI, language?: string | undefined): boolean { throw new Error('Method not implemented.'); } }; + +export class TestWorkspaceTrustEnablementService implements IWorkspaceTrustEnablementService { + _serviceBrand: undefined; + + constructor(private isEnabled: boolean = true) { } + + isWorkspaceTrustEnabled(): boolean { + return this.isEnabled; + } +} + +export class TestWorkspaceTrustManagementService extends Disposable implements IWorkspaceTrustManagementService { + _serviceBrand: undefined; + + private _onDidChangeTrust = this._register(new Emitter()); + onDidChangeTrust = this._onDidChangeTrust.event; + + private _onDidChangeTrustedFolders = this._register(new Emitter()); + onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event; + + private _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter()); + onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; + + + constructor( + private trusted: boolean = true + ) { + super(); + } + + get acceptsOutOfWorkspaceFiles(): boolean { + throw new Error('Method not implemented.'); + } + + set acceptsOutOfWorkspaceFiles(value: boolean) { + throw new Error('Method not implemented.'); + } + + addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable { + throw new Error('Method not implemented.'); + } + + getTrustedUris(): URI[] { + throw new Error('Method not implemented.'); + } + + setParentFolderTrust(trusted: boolean): Promise { + throw new Error('Method not implemented.'); + } + + getUriTrustInfo(uri: URI): Promise { + throw new Error('Method not implemented.'); + } + + async setTrustedUris(folders: URI[]): Promise { + throw new Error('Method not implemented.'); + } + + async setUrisTrust(uris: URI[], trusted: boolean): Promise { + throw new Error('Method not implemented.'); + } + + canSetParentFolderTrust(): boolean { + throw new Error('Method not implemented.'); + } + + canSetWorkspaceTrust(): boolean { + throw new Error('Method not implemented.'); + } + + isWorkspaceTrusted(): boolean { + return this.trusted; + } + + isWorkspaceTrustForced(): boolean { + return false; + } + + get workspaceTrustInitialized(): Promise { + return Promise.resolve(); + } + + get workspaceResolved(): Promise { + return Promise.resolve(); + } + + async setWorkspaceTrust(trusted: boolean): Promise { + if (this.trusted !== trusted) { + this.trusted = trusted; + this._onDidChangeTrust.fire(this.trusted); + } + } +} + +export class TestWorkspaceTrustRequestService extends Disposable implements IWorkspaceTrustRequestService { + _serviceBrand: any; + + private readonly _onDidInitiateOpenFilesTrustRequest = this._register(new Emitter()); + readonly onDidInitiateOpenFilesTrustRequest = this._onDidInitiateOpenFilesTrustRequest.event; + + private readonly _onDidInitiateWorkspaceTrustRequest = this._register(new Emitter()); + readonly onDidInitiateWorkspaceTrustRequest = this._onDidInitiateWorkspaceTrustRequest.event; + + private readonly _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter()); + readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; + + constructor(private readonly _trusted: boolean) { + super(); + } + + requestOpenUrisHandler = async (uris: URI[]) => { + return WorkspaceTrustUriResponse.Open; + }; + + requestOpenFilesTrust(uris: URI[]): Promise { + return this.requestOpenUrisHandler(uris); + } + + async completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse: boolean): Promise { + throw new Error('Method not implemented.'); + } + + cancelWorkspaceTrustRequest(): void { + throw new Error('Method not implemented.'); + } + + async completeWorkspaceTrustRequest(trusted?: boolean): Promise { + throw new Error('Method not implemented.'); + } + + async requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise { + return this._trusted; + } + + requestWorkspaceTrustOnStartup(): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index 1a40dc62bd5..b88818f68fa 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -8,7 +8,7 @@ import { workbenchInstantiationService as browserWorkbenchInstantiationService, import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { INativeHostService, IOSProperties, IOSStatistics } from 'vs/platform/native/common/native'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IFileDialogService, INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; @@ -52,11 +52,8 @@ export class TestSharedProcessService implements ISharedProcessService { declare readonly _serviceBrand: undefined; createRawConnection(): never { throw new Error('Not Implemented'); } - getChannel(channelName: string): any { return undefined; } - registerChannel(channelName: string, channel: any): void { } - notifyRestored(): void { } } @@ -178,7 +175,7 @@ export function workbenchInstantiationService(overrides?: { textEditorService?: (instantiationService: IInstantiationService) => ITextEditorService; }, disposables = new DisposableStore()): ITestInstantiationService { const instantiationService = browserWorkbenchInstantiationService({ - workingCopyBackupService: (instantiationService: IInstantiationService) => new TestNativeWorkingCopyBackupService(), + workingCopyBackupService: () => disposables.add(new TestNativeWorkingCopyBackupService()), ...overrides }, disposables); @@ -216,7 +213,7 @@ export class TestNativeTextFileServiceWithEncodingOverrides extends NativeTextFi } } -export class TestNativeWorkingCopyBackupService extends NativeWorkingCopyBackupService { +export class TestNativeWorkingCopyBackupService extends NativeWorkingCopyBackupService implements IDisposable { private backupResourceJoiners: Function[]; private discardBackupJoiners: Function[]; @@ -231,15 +228,18 @@ export class TestNativeWorkingCopyBackupService extends NativeWorkingCopyBackupS const lifecycleService = new TestLifecycleService(); super(environmentService as any, fileService, logService, lifecycleService); - const inMemoryFileSystemProvider = new InMemoryFileSystemProvider(); - fileService.registerProvider(Schemas.inMemory, inMemoryFileSystemProvider); - fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, inMemoryFileSystemProvider, Schemas.vscodeUserData, logService)); + const inMemoryFileSystemProvider = this._register(new InMemoryFileSystemProvider()); + this._register(fileService.registerProvider(Schemas.inMemory, inMemoryFileSystemProvider)); + this._register(fileService.registerProvider(Schemas.vscodeUserData, this._register(new FileUserDataProvider(Schemas.file, inMemoryFileSystemProvider, Schemas.vscodeUserData, logService)))); this.backupResourceJoiners = []; this.discardBackupJoiners = []; this.discardedBackups = []; this.pendingBackupsArr = []; this.discardedAllBackups = false; + + this._register(fileService); + this._register(lifecycleService); } testGetFileService(): IFileService {