Path: blob/main/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { Event } from '../../../../base/common/event.js';7import { DisposableStore, IReference, ImmortalReference } from '../../../../base/common/lifecycle.js';8import { URI } from '../../../../base/common/uri.js';9import { mock } from '../../../../base/test/common/mock.js';10import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';11import { IBulkEditService } from '../../../../editor/browser/services/bulkEditService.js';12import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';13import { EditOperation } from '../../../../editor/common/core/editOperation.js';14import { Position } from '../../../../editor/common/core/position.js';15import { Range } from '../../../../editor/common/core/range.js';16import { ITextSnapshot } from '../../../../editor/common/model.js';17import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js';18import { IModelService } from '../../../../editor/common/services/model.js';19import { ModelService } from '../../../../editor/common/services/modelService.js';20import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js';21import { TestCodeEditorService } from '../../../../editor/test/browser/editorTestServices.js';22import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';23import { TestConfigurationService } from '../../../../platform/configuration/test/common/testConfigurationService.js';24import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';25import { TestDialogService } from '../../../../platform/dialogs/test/common/testDialogService.js';26import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';27import { IFileService } from '../../../../platform/files/common/files.js';28import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';29import { InstantiationService } from '../../../../platform/instantiation/common/instantiationService.js';30import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';31import { ILabelService } from '../../../../platform/label/common/label.js';32import { ILogService, NullLogService } from '../../../../platform/log/common/log.js';33import { INotificationService } from '../../../../platform/notification/common/notification.js';34import { TestNotificationService } from '../../../../platform/notification/test/common/testNotificationService.js';35import { TestThemeService } from '../../../../platform/theme/test/common/testThemeService.js';36import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js';37import { UndoRedoService } from '../../../../platform/undoRedo/common/undoRedoService.js';38import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';39import { UriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentityService.js';40import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';41import { MainThreadBulkEdits } from '../../browser/mainThreadBulkEdits.js';42import { IWorkspaceTextEditDto } from '../../common/extHost.protocol.js';43import { SingleProxyRPCProtocol } from '../common/testRPCProtocol.js';44import { BulkEditService } from '../../../contrib/bulkEdit/browser/bulkEditService.js';45import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';46import { IEditorService } from '../../../services/editor/common/editorService.js';47import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';48import { SerializableObjectWithBuffers } from '../../../services/extensions/common/proxyIdentifier.js';49import { LabelService } from '../../../services/label/common/labelService.js';50import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js';51import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js';52import { ITextFileService } from '../../../services/textfile/common/textfiles.js';53import { ICopyOperation, ICreateFileOperation, ICreateOperation, IDeleteOperation, IMoveOperation, IWorkingCopyFileService } from '../../../services/workingCopy/common/workingCopyFileService.js';54import { IWorkingCopyService } from '../../../services/workingCopy/common/workingCopyService.js';55import { TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestFileService, TestLifecycleService, TestWorkingCopyService } from '../../../test/browser/workbenchTestServices.js';56import { TestContextService, TestTextResourcePropertiesService } from '../../../test/common/workbenchTestServices.js';57import { ILanguageService } from '../../../../editor/common/languages/language.js';58import { LanguageService } from '../../../../editor/common/services/languageService.js';59import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js';60import { TestLanguageConfigurationService } from '../../../../editor/test/common/modes/testLanguageConfigurationService.js';61import { ITreeSitterLibraryService } from '../../../../editor/common/services/treeSitter/treeSitterLibraryService.js';62import { TestTreeSitterLibraryService } from '../../../../editor/test/common/services/testTreeSitterLibraryService.js';6364suite('MainThreadEditors', () => {6566let disposables: DisposableStore;67const resource = URI.parse('foo:bar');6869let modelService: IModelService;7071let bulkEdits: MainThreadBulkEdits;7273const movedResources = new Map<URI, URI>();74const copiedResources = new Map<URI, URI>();75const createdResources = new Set<URI>();76const deletedResources = new Set<URI>();7778setup(() => {79disposables = new DisposableStore();8081movedResources.clear();82copiedResources.clear();83createdResources.clear();84deletedResources.clear();8586const configService = new TestConfigurationService();87const dialogService = new TestDialogService();88const notificationService = new TestNotificationService();89const undoRedoService = new UndoRedoService(dialogService, notificationService);90const themeService = new TestThemeService();9192const services = new ServiceCollection();93services.set(IBulkEditService, new SyncDescriptor(BulkEditService));94services.set(ILabelService, new SyncDescriptor(LabelService));95services.set(ILogService, new NullLogService());96services.set(IWorkspaceContextService, new TestContextService());97services.set(IEnvironmentService, TestEnvironmentService);98services.set(IWorkbenchEnvironmentService, TestEnvironmentService);99services.set(IConfigurationService, configService);100services.set(IDialogService, dialogService);101services.set(INotificationService, notificationService);102services.set(IUndoRedoService, undoRedoService);103services.set(IModelService, modelService);104services.set(ICodeEditorService, new TestCodeEditorService(themeService));105services.set(IFileService, new TestFileService());106services.set(IUriIdentityService, new SyncDescriptor(UriIdentityService));107services.set(ITreeSitterLibraryService, new TestTreeSitterLibraryService());108services.set(IEditorService, disposables.add(new TestEditorService()));109services.set(ILifecycleService, new TestLifecycleService());110services.set(IWorkingCopyService, new TestWorkingCopyService());111services.set(IEditorGroupsService, new TestEditorGroupsService());112services.set(ITextFileService, new class extends mock<ITextFileService>() {113override isDirty() { return false; }114override files = <any>{115onDidSave: Event.None,116onDidRevert: Event.None,117onDidChangeDirty: Event.None118};119override create(operations: { resource: URI }[]) {120for (const o of operations) {121createdResources.add(o.resource);122}123return Promise.resolve(Object.create(null));124}125override async getEncodedReadable(resource: URI, value?: string | ITextSnapshot): Promise<any> {126return undefined;127}128});129services.set(IWorkingCopyFileService, new class extends mock<IWorkingCopyFileService>() {130override onDidRunWorkingCopyFileOperation = Event.None;131override createFolder(operations: ICreateOperation[]): any {132this.create(operations);133}134override create(operations: ICreateFileOperation[]) {135for (const operation of operations) {136createdResources.add(operation.resource);137}138return Promise.resolve(Object.create(null));139}140override move(operations: IMoveOperation[]) {141const { source, target } = operations[0].file;142movedResources.set(source, target);143return Promise.resolve(Object.create(null));144}145override copy(operations: ICopyOperation[]) {146const { source, target } = operations[0].file;147copiedResources.set(source, target);148return Promise.resolve(Object.create(null));149}150override delete(operations: IDeleteOperation[]) {151for (const operation of operations) {152deletedResources.add(operation.resource);153}154return Promise.resolve(undefined);155}156});157services.set(ITextModelService, new class extends mock<ITextModelService>() {158override createModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {159const textEditorModel = new class extends mock<IResolvedTextEditorModel>() {160override textEditorModel = modelService.getModel(resource)!;161};162textEditorModel.isReadonly = () => false;163return Promise.resolve(new ImmortalReference(textEditorModel));164}165});166services.set(IEditorWorkerService, new class extends mock<IEditorWorkerService>() {167168});169services.set(IPaneCompositePartService, new class extends mock<IPaneCompositePartService>() implements IPaneCompositePartService {170override onDidPaneCompositeOpen = Event.None;171override onDidPaneCompositeClose = Event.None;172override getActivePaneComposite() {173return undefined;174}175});176177services.set(ILanguageService, disposables.add(new LanguageService()));178services.set(ILanguageConfigurationService, new TestLanguageConfigurationService());179180const instaService = new InstantiationService(services);181182modelService = new ModelService(183configService,184new TestTextResourcePropertiesService(configService),185undoRedoService,186instaService187);188189bulkEdits = instaService.createInstance(MainThreadBulkEdits, SingleProxyRPCProtocol(null));190});191192teardown(() => {193disposables.dispose();194});195196ensureNoDisposablesAreLeakedInTestSuite();197198test(`applyWorkspaceEdit returns false if model is changed by user`, () => {199200const model = disposables.add(modelService.createModel('something', null, resource));201202const workspaceResourceEdit: IWorkspaceTextEditDto = {203resource: resource,204versionId: model.getVersionId(),205textEdit: {206text: 'asdfg',207range: new Range(1, 1, 1, 1)208}209};210211// Act as if the user edited the model212model.applyEdits([EditOperation.insert(new Position(0, 0), 'something')]);213214return bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({ edits: [workspaceResourceEdit] })).then((result) => {215assert.strictEqual(result, false);216});217});218219test(`issue #54773: applyWorkspaceEdit checks model version in race situation`, () => {220221const model = disposables.add(modelService.createModel('something', null, resource));222223const workspaceResourceEdit1: IWorkspaceTextEditDto = {224resource: resource,225versionId: model.getVersionId(),226textEdit: {227text: 'asdfg',228range: new Range(1, 1, 1, 1)229}230};231const workspaceResourceEdit2: IWorkspaceTextEditDto = {232resource: resource,233versionId: model.getVersionId(),234textEdit: {235text: 'asdfg',236range: new Range(1, 1, 1, 1)237}238};239240const p1 = bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({ edits: [workspaceResourceEdit1] })).then((result) => {241// first edit request succeeds242assert.strictEqual(result, true);243});244const p2 = bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({ edits: [workspaceResourceEdit2] })).then((result) => {245// second edit request fails246assert.strictEqual(result, false);247});248return Promise.all([p1, p2]);249});250251test(`applyWorkspaceEdit with only resource edit`, () => {252return bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({253edits: [254{ oldResource: resource, newResource: resource, options: undefined },255{ oldResource: undefined, newResource: resource, options: undefined },256{ oldResource: resource, newResource: undefined, options: undefined }257]258})).then((result) => {259assert.strictEqual(result, true);260assert.strictEqual(movedResources.get(resource), resource);261assert.strictEqual(createdResources.has(resource), true);262assert.strictEqual(deletedResources.has(resource), true);263});264});265});266267268