Path: blob/main/src/vs/workbench/test/common/workbenchTestServices.ts
5239 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 { DeferredPromise, timeout } from '../../../base/common/async.js';6import { bufferToStream, readableToBuffer, VSBuffer, VSBufferReadable } from '../../../base/common/buffer.js';7import { CancellationToken } from '../../../base/common/cancellation.js';8import { Emitter, Event } from '../../../base/common/event.js';9import { Iterable } from '../../../base/common/iterator.js';10import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';11import { ResourceMap } from '../../../base/common/map.js';12import { Schemas } from '../../../base/common/network.js';13import { observableValue } from '../../../base/common/observable.js';14import { join } from '../../../base/common/path.js';15import { isLinux, isMacintosh } from '../../../base/common/platform.js';16import { basename, isEqual, isEqualOrParent } from '../../../base/common/resources.js';17import { URI } from '../../../base/common/uri.js';18import { ITextResourcePropertiesService } from '../../../editor/common/services/textResourceConfiguration.js';19import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';20import { IResourceEditorInput } from '../../../platform/editor/common/editor.js';21import { FileChangesEvent, FileOperationEvent, FileSystemProviderCapabilities, IBaseFileStat, ICreateFileOptions, IFileContent, IFileService, IFileStat, IFileStatResult, IFileStatWithMetadata, IFileStatWithPartialMetadata, IFileStreamContent, IFileSystemProvider, IFileSystemProviderActivationEvent, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemWatcher, IReadFileOptions, IReadFileStreamOptions, IResolveFileOptions, IResolveMetadataFileOptions, IWatchOptions, IWatchOptionsWithCorrelation, IWriteFileOptions } from '../../../platform/files/common/files.js';22import { AbstractLoggerService, ILogger, LogLevel, NullLogger } from '../../../platform/log/common/log.js';23import { IMarker, IMarkerData, IMarkerService, IResourceMarker, MarkerStatistics } from '../../../platform/markers/common/markers.js';24import product from '../../../platform/product/common/product.js';25import { IProgress, IProgressStep } from '../../../platform/progress/common/progress.js';26import { InMemoryStorageService, WillSaveStateReason } from '../../../platform/storage/common/storage.js';27import { toUserDataProfile } from '../../../platform/userDataProfile/common/userDataProfile.js';28import { ISingleFolderWorkspaceIdentifier, IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, IWorkspaceFoldersWillChangeEvent, IWorkspaceIdentifier, WorkbenchState, Workspace } from '../../../platform/workspace/common/workspace.js';29import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo, ResourceTrustRequestOptions, WorkspaceTrustRequestOptions, WorkspaceTrustUriResponse } from '../../../platform/workspace/common/workspaceTrust.js';30import { TestWorkspace } from '../../../platform/workspace/test/common/testWorkspace.js';31import { GroupIdentifier, IRevertOptions, ISaveOptions, SaveReason } from '../../common/editor.js';32import { EditorInput } from '../../common/editor/editorInput.js';33import { IActivity, IActivityService } from '../../services/activity/common/activity.js';34import { ChatEntitlement, IChatEntitlementService } from '../../services/chat/common/chatEntitlementService.js';35import { NullExtensionService } from '../../services/extensions/common/extensions.js';36import { IAutoSaveConfiguration, IAutoSaveMode, IFilesConfigurationService } from '../../services/filesConfiguration/common/filesConfigurationService.js';37import { IHistoryService } from '../../services/history/common/history.js';38import { BeforeShutdownErrorEvent, ILifecycleService, InternalBeforeShutdownEvent, LifecyclePhase, ShutdownReason, StartupKind, WillShutdownEvent } from '../../services/lifecycle/common/lifecycle.js';39import { IResourceEncoding } from '../../services/textfile/common/textfiles.js';40import { IUserDataProfileService } from '../../services/userDataProfile/common/userDataProfile.js';41import { IStoredFileWorkingCopySaveEvent } from '../../services/workingCopy/common/storedFileWorkingCopy.js';42import { IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from '../../services/workingCopy/common/workingCopy.js';43import { ICopyOperation, ICreateFileOperation, ICreateOperation, IDeleteOperation, IFileOperationUndoRedoInfo, IMoveOperation, IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext, IWorkingCopyFileOperationParticipant, IWorkingCopyFileService, WorkingCopyFileEvent } from '../../services/workingCopy/common/workingCopyFileService.js';4445export class TestLoggerService extends AbstractLoggerService {46constructor(logsHome?: URI) {47super(LogLevel.Info, logsHome ?? URI.file('tests').with({ scheme: 'vscode-tests' }));48}49protected doCreateLogger(): ILogger { return new NullLogger(); }50}5152export class TestTextResourcePropertiesService implements ITextResourcePropertiesService {5354declare readonly _serviceBrand: undefined;5556constructor(57@IConfigurationService private readonly configurationService: IConfigurationService,58) {59}6061getEOL(resource: URI, language?: string): string {62const eol = this.configurationService.getValue('files.eol', { overrideIdentifier: language, resource });63if (eol && typeof eol === 'string' && eol !== 'auto') {64return eol;65}66return (isLinux || isMacintosh) ? '\n' : '\r\n';67}68}6970export class TestUserDataProfileService implements IUserDataProfileService {7172readonly _serviceBrand: undefined;73readonly onDidChangeCurrentProfile = Event.None;74readonly currentProfile = toUserDataProfile('test', 'test', URI.file('tests').with({ scheme: 'vscode-tests' }), URI.file('tests').with({ scheme: 'vscode-tests' }));75async updateCurrentProfile(): Promise<void> { }76}7778export class TestContextService implements IWorkspaceContextService {7980declare readonly _serviceBrand: undefined;8182private workspace: Workspace;83private options: object;8485private readonly _onDidChangeWorkspaceName: Emitter<void>;86get onDidChangeWorkspaceName(): Event<void> { return this._onDidChangeWorkspaceName.event; }8788private readonly _onWillChangeWorkspaceFolders: Emitter<IWorkspaceFoldersWillChangeEvent>;89get onWillChangeWorkspaceFolders(): Event<IWorkspaceFoldersWillChangeEvent> { return this._onWillChangeWorkspaceFolders.event; }9091private readonly _onDidChangeWorkspaceFolders: Emitter<IWorkspaceFoldersChangeEvent>;92get onDidChangeWorkspaceFolders(): Event<IWorkspaceFoldersChangeEvent> { return this._onDidChangeWorkspaceFolders.event; }9394private readonly _onDidChangeWorkbenchState: Emitter<WorkbenchState>;95get onDidChangeWorkbenchState(): Event<WorkbenchState> { return this._onDidChangeWorkbenchState.event; }9697constructor(workspace = TestWorkspace, options = null) {98this.workspace = workspace;99this.options = options || Object.create(null);100this._onDidChangeWorkspaceName = new Emitter<void>();101this._onWillChangeWorkspaceFolders = new Emitter<IWorkspaceFoldersWillChangeEvent>();102this._onDidChangeWorkspaceFolders = new Emitter<IWorkspaceFoldersChangeEvent>();103this._onDidChangeWorkbenchState = new Emitter<WorkbenchState>();104}105106getFolders(): IWorkspaceFolder[] {107return this.workspace ? this.workspace.folders : [];108}109110getWorkbenchState(): WorkbenchState {111if (this.workspace.configuration) {112return WorkbenchState.WORKSPACE;113}114115if (this.workspace.folders.length) {116return WorkbenchState.FOLDER;117}118119return WorkbenchState.EMPTY;120}121122getCompleteWorkspace(): Promise<IWorkspace> {123return Promise.resolve(this.getWorkspace());124}125126getWorkspace(): IWorkspace {127return this.workspace;128}129130getWorkspaceFolder(resource: URI): IWorkspaceFolder | null {131return this.workspace.getFolder(resource);132}133134setWorkspace(workspace: any): void {135this.workspace = workspace;136}137138getOptions() {139return this.options;140}141142updateOptions() { }143144isInsideWorkspace(resource: URI): boolean {145if (resource && this.workspace) {146return isEqualOrParent(resource, this.workspace.folders[0].uri);147}148149return false;150}151152toResource(workspaceRelativePath: string): URI {153return URI.file(join('C:\\', workspaceRelativePath));154}155156isCurrentWorkspace(workspaceIdOrFolder: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): boolean {157return URI.isUri(workspaceIdOrFolder) && isEqual(this.workspace.folders[0].uri, workspaceIdOrFolder);158}159}160161export class TestStorageService extends InMemoryStorageService {162163testEmitWillSaveState(reason: WillSaveStateReason): void {164super.emitWillSaveState(reason);165}166}167168export class TestHistoryService implements IHistoryService {169170declare readonly _serviceBrand: undefined;171172constructor(private root?: URI) { }173174async reopenLastClosedEditor(): Promise<void> { }175async goForward(): Promise<void> { }176async goBack(): Promise<void> { }177async goPrevious(): Promise<void> { }178async goLast(): Promise<void> { }179removeFromHistory(_input: EditorInput | IResourceEditorInput): void { }180clear(): void { }181clearRecentlyOpened(): void { }182getHistory(): readonly (EditorInput | IResourceEditorInput)[] { return []; }183async openNextRecentlyUsedEditor(group?: GroupIdentifier): Promise<void> { }184async openPreviouslyUsedEditor(group?: GroupIdentifier): Promise<void> { }185getLastActiveWorkspaceRoot(_schemeFilter: string): URI | undefined { return this.root; }186getLastActiveFile(_schemeFilter: string): URI | undefined { return undefined; }187}188189export class TestWorkingCopy extends Disposable implements IWorkingCopy {190191private readonly _onDidChangeDirty = this._register(new Emitter<void>());192readonly onDidChangeDirty = this._onDidChangeDirty.event;193194private readonly _onDidChangeContent = this._register(new Emitter<void>());195readonly onDidChangeContent = this._onDidChangeContent.event;196197private readonly _onDidSave = this._register(new Emitter<IStoredFileWorkingCopySaveEvent>());198readonly onDidSave = this._onDidSave.event;199200readonly capabilities = WorkingCopyCapabilities.None;201202readonly name;203204private dirty = false;205206constructor(readonly resource: URI, isDirty = false, readonly typeId = 'testWorkingCopyType') {207super();208209this.name = basename(this.resource);210this.dirty = isDirty;211}212213setDirty(dirty: boolean): void {214if (this.dirty !== dirty) {215this.dirty = dirty;216this._onDidChangeDirty.fire();217}218}219220setContent(content: string): void {221this._onDidChangeContent.fire();222}223224isDirty(): boolean {225return this.dirty;226}227228isModified(): boolean {229return this.isDirty();230}231232async save(options?: ISaveOptions, stat?: IFileStatWithMetadata): Promise<boolean> {233this._onDidSave.fire({ reason: options?.reason ?? SaveReason.EXPLICIT, stat: stat ?? createFileStat(this.resource), source: options?.source });234235return true;236}237238async revert(options?: IRevertOptions): Promise<void> {239this.setDirty(false);240}241242async backup(token: CancellationToken): Promise<IWorkingCopyBackup> {243return {};244}245}246247export function createFileStat(resource: URI, readonly = false, isFile?: boolean, isDirectory?: boolean, isSymbolicLink?: boolean, children?: { resource: URI; isFile?: boolean; isDirectory?: boolean; isSymbolicLink?: boolean; executable?: boolean }[] | undefined, executable?: boolean): IFileStatWithMetadata {248return {249resource,250etag: Date.now().toString(),251mtime: Date.now(),252ctime: Date.now(),253size: 42,254isFile: isFile ?? true,255isDirectory: isDirectory ?? false,256isSymbolicLink: isSymbolicLink ?? false,257readonly,258locked: false,259executable: executable ?? false,260name: basename(resource),261children: children?.map(c => createFileStat(c.resource, false, c.isFile, c.isDirectory, c.isSymbolicLink, undefined, c.executable)),262};263}264265export class TestWorkingCopyFileService implements IWorkingCopyFileService {266267declare readonly _serviceBrand: undefined;268269readonly onWillRunWorkingCopyFileOperation: Event<WorkingCopyFileEvent> = Event.None;270readonly onDidFailWorkingCopyFileOperation: Event<WorkingCopyFileEvent> = Event.None;271readonly onDidRunWorkingCopyFileOperation: Event<WorkingCopyFileEvent> = Event.None;272273addFileOperationParticipant(participant: IWorkingCopyFileOperationParticipant): IDisposable { return Disposable.None; }274275readonly hasSaveParticipants = false;276addSaveParticipant(participant: IStoredFileWorkingCopySaveParticipant): IDisposable { return Disposable.None; }277async runSaveParticipants(workingCopy: IWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> { }278279async delete(operations: IDeleteOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<void> { }280281registerWorkingCopyProvider(provider: (resourceOrFolder: URI) => IWorkingCopy[]): IDisposable { return Disposable.None; }282283getDirty(resource: URI): IWorkingCopy[] { return []; }284285create(operations: ICreateFileOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }286createFolder(operations: ICreateOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }287288move(operations: IMoveOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }289290copy(operations: ICopyOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }291}292293export function mock<T>(): Ctor<T> {294// eslint-disable-next-line local/code-no-any-casts295return function () { } as any;296}297298export interface Ctor<T> {299new(): T;300}301302export class TestExtensionService extends NullExtensionService { }303304export const TestProductService = { _serviceBrand: undefined, ...product };305306export class TestActivityService implements IActivityService {307_serviceBrand: undefined;308onDidChangeActivity = Event.None;309getViewContainerActivities(viewContainerId: string): IActivity[] {310return [];311}312getActivity(id: string): IActivity[] {313return [];314}315showViewContainerActivity(viewContainerId: string, badge: IActivity): IDisposable {316return this;317}318showViewActivity(viewId: string, badge: IActivity): IDisposable {319return this;320}321showAccountsActivity(activity: IActivity): IDisposable {322return this;323}324showGlobalActivity(activity: IActivity): IDisposable {325return this;326}327328dispose() { }329}330331export const NullFilesConfigurationService = new class implements IFilesConfigurationService {332333_serviceBrand: undefined;334335readonly onDidChangeAutoSaveConfiguration = Event.None;336readonly onDidChangeAutoSaveDisabled = Event.None;337readonly onDidChangeReadonly = Event.None;338readonly onDidChangeFilesAssociation = Event.None;339340readonly isHotExitEnabled = false;341readonly hotExitConfiguration = undefined;342343getAutoSaveConfiguration(): IAutoSaveConfiguration { throw new Error('Method not implemented.'); }344getAutoSaveMode(): IAutoSaveMode { throw new Error('Method not implemented.'); }345hasShortAutoSaveDelay(): boolean { throw new Error('Method not implemented.'); }346toggleAutoSave(): Promise<void> { throw new Error('Method not implemented.'); }347enableAutoSaveAfterShortDelay(resourceOrEditor: URI | EditorInput): IDisposable { throw new Error('Method not implemented.'); }348disableAutoSave(resourceOrEditor: URI | EditorInput): IDisposable { throw new Error('Method not implemented.'); }349isReadonly(resource: URI, stat?: IBaseFileStat | undefined): boolean { return false; }350async updateReadonly(resource: URI, readonly: boolean | 'toggle' | 'reset'): Promise<void> { }351preventSaveConflicts(resource: URI, language?: string | undefined): boolean { throw new Error('Method not implemented.'); }352};353354export class TestWorkspaceTrustEnablementService implements IWorkspaceTrustEnablementService {355_serviceBrand: undefined;356357constructor(private isEnabled: boolean = true) { }358359isWorkspaceTrustEnabled(): boolean {360return this.isEnabled;361}362}363364export class TestWorkspaceTrustManagementService extends Disposable implements IWorkspaceTrustManagementService {365_serviceBrand: undefined;366367private _onDidChangeTrust = this._register(new Emitter<boolean>());368onDidChangeTrust = this._onDidChangeTrust.event;369370private _onDidChangeTrustedFolders = this._register(new Emitter<void>());371onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event;372373private _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter<void>());374onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event;375376377constructor(378private trusted: boolean = true379) {380super();381}382383get acceptsOutOfWorkspaceFiles(): boolean {384throw new Error('Method not implemented.');385}386387set acceptsOutOfWorkspaceFiles(value: boolean) {388throw new Error('Method not implemented.');389}390391addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable {392throw new Error('Method not implemented.');393}394395getTrustedUris(): URI[] {396throw new Error('Method not implemented.');397}398399setParentFolderTrust(trusted: boolean): Promise<void> {400throw new Error('Method not implemented.');401}402403getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo> {404throw new Error('Method not implemented.');405}406407async setTrustedUris(folders: URI[]): Promise<void> {408throw new Error('Method not implemented.');409}410411async setUrisTrust(uris: URI[], trusted: boolean): Promise<void> {412throw new Error('Method not implemented.');413}414415canSetParentFolderTrust(): boolean {416throw new Error('Method not implemented.');417}418419canSetWorkspaceTrust(): boolean {420throw new Error('Method not implemented.');421}422423isWorkspaceTrusted(): boolean {424return this.trusted;425}426427isWorkspaceTrustForced(): boolean {428return false;429}430431get workspaceTrustInitialized(): Promise<void> {432return Promise.resolve();433}434435get workspaceResolved(): Promise<void> {436return Promise.resolve();437}438439async setWorkspaceTrust(trusted: boolean): Promise<void> {440if (this.trusted !== trusted) {441this.trusted = trusted;442this._onDidChangeTrust.fire(this.trusted);443}444}445}446447export class TestWorkspaceTrustRequestService extends Disposable implements IWorkspaceTrustRequestService {448_serviceBrand: any;449450private readonly _onDidInitiateOpenFilesTrustRequest = this._register(new Emitter<void>());451readonly onDidInitiateOpenFilesTrustRequest = this._onDidInitiateOpenFilesTrustRequest.event;452453private readonly _onDidInitiateResourcesTrustRequest = this._register(new Emitter<ResourceTrustRequestOptions>());454readonly onDidInitiateResourcesTrustRequest = this._onDidInitiateResourcesTrustRequest.event;455456private readonly _onDidInitiateWorkspaceTrustRequest = this._register(new Emitter<WorkspaceTrustRequestOptions>());457readonly onDidInitiateWorkspaceTrustRequest = this._onDidInitiateWorkspaceTrustRequest.event;458459private readonly _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter<void>());460readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event;461462constructor(private readonly _trusted: boolean) {463super();464}465466requestOpenUrisHandler = async (uris: URI[]) => {467return WorkspaceTrustUriResponse.Open;468};469470requestOpenFilesTrust(uris: URI[]): Promise<WorkspaceTrustUriResponse> {471return this.requestOpenUrisHandler(uris);472}473474async completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse: boolean): Promise<void> {475throw new Error('Method not implemented.');476}477478async completeResourcesTrustRequest(uri: URI, result: WorkspaceTrustUriResponse): Promise<void> {479throw new Error('Method not implemented.');480}481482async requestResourcesTrust(options: ResourceTrustRequestOptions): Promise<boolean | undefined> {483return this._trusted;484}485486cancelWorkspaceTrustRequest(): void {487throw new Error('Method not implemented.');488}489490async completeWorkspaceTrustRequest(trusted?: boolean): Promise<void> {491throw new Error('Method not implemented.');492}493494async requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean> {495return this._trusted;496}497498requestWorkspaceTrustOnStartup(): void {499throw new Error('Method not implemented.');500}501}502503export class TestMarkerService implements IMarkerService {504505_serviceBrand: undefined;506507onMarkerChanged = Event.None;508509getStatistics(): MarkerStatistics { throw new Error('Method not implemented.'); }510changeOne(owner: string, resource: URI, markers: IMarkerData[]): void { }511changeAll(owner: string, data: IResourceMarker[]): void { }512remove(owner: string, resources: URI[]): void { }513read(filter?: { owner?: string | undefined; resource?: URI | undefined; severities?: number | undefined; take?: number | undefined } | undefined): IMarker[] { return []; }514installResourceFilter(resource: URI, reason: string): IDisposable {515return { dispose: () => { /* TODO: Implement cleanup logic */ } };516}517}518519export class TestFileService implements IFileService {520521declare readonly _serviceBrand: undefined;522523private readonly _onDidFilesChange = new Emitter<FileChangesEvent>();524get onDidFilesChange(): Event<FileChangesEvent> { return this._onDidFilesChange.event; }525fireFileChanges(event: FileChangesEvent): void { this._onDidFilesChange.fire(event); }526527private readonly _onDidRunOperation = new Emitter<FileOperationEvent>();528get onDidRunOperation(): Event<FileOperationEvent> { return this._onDidRunOperation.event; }529fireAfterOperation(event: FileOperationEvent): void { this._onDidRunOperation.fire(event); }530531private readonly _onDidChangeFileSystemProviderCapabilities = new Emitter<IFileSystemProviderCapabilitiesChangeEvent>();532get onDidChangeFileSystemProviderCapabilities(): Event<IFileSystemProviderCapabilitiesChangeEvent> { return this._onDidChangeFileSystemProviderCapabilities.event; }533fireFileSystemProviderCapabilitiesChangeEvent(event: IFileSystemProviderCapabilitiesChangeEvent): void { this._onDidChangeFileSystemProviderCapabilities.fire(event); }534535private _onWillActivateFileSystemProvider = new Emitter<IFileSystemProviderActivationEvent>();536readonly onWillActivateFileSystemProvider = this._onWillActivateFileSystemProvider.event;537readonly onDidWatchError = Event.None;538539protected content = 'Hello Html';540protected lastReadFileUri!: URI;541542readonly = false;543544// Tracking functionality for tests545readonly writeOperations: Array<{ resource: URI; content: string }> = [];546readonly readOperations: Array<{ resource: URI }> = [];547548setContent(content: string): void { this.content = content; }549getContent(): string { return this.content; }550getLastReadFileUri(): URI { return this.lastReadFileUri; }551552// Clear tracking data for tests553clearTracking(): void {554this.writeOperations.length = 0;555this.readOperations.length = 0;556}557558resolve(resource: URI, _options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;559resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat>;560async resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat> {561return createFileStat(resource, this.readonly);562}563564stat(resource: URI): Promise<IFileStatWithPartialMetadata> {565return this.resolve(resource, { resolveMetadata: true });566}567568async realpath(resource: URI): Promise<URI> {569return resource;570}571572async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]> {573const stats = await Promise.all(toResolve.map(resourceAndOption => this.resolve(resourceAndOption.resource, resourceAndOption.options)));574575return stats.map(stat => ({ stat, success: true }));576}577578readonly notExistsSet = new ResourceMap<boolean>();579580async exists(_resource: URI): Promise<boolean> { return !this.notExistsSet.has(_resource); }581582readShouldThrowError: Error | undefined = undefined;583584async readFile(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileContent> {585if (this.readShouldThrowError) {586throw this.readShouldThrowError;587}588589this.lastReadFileUri = resource;590this.readOperations.push({ resource });591592return {593...createFileStat(resource, this.readonly),594value: VSBuffer.fromString(this.content)595};596}597598async readFileStream(resource: URI, options?: IReadFileStreamOptions | undefined): Promise<IFileStreamContent> {599if (this.readShouldThrowError) {600throw this.readShouldThrowError;601}602603this.lastReadFileUri = resource;604605return {606...createFileStat(resource, this.readonly),607value: bufferToStream(VSBuffer.fromString(this.content))608};609}610611writeShouldThrowError: Error | undefined = undefined;612613async writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata> {614await timeout(0);615616if (this.writeShouldThrowError) {617throw this.writeShouldThrowError;618}619620let content: VSBuffer | undefined;621if (bufferOrReadable instanceof VSBuffer) {622content = bufferOrReadable;623} else {624try {625content = readableToBuffer(bufferOrReadable);626} catch {627// Some preexisting tests are writing with invalid objects628}629}630631if (content) {632this.writeOperations.push({ resource, content: content.toString() });633}634635return createFileStat(resource, this.readonly);636}637638move(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }639copy(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }640async cloneFile(_source: URI, _target: URI): Promise<void> { }641createFile(_resource: URI, _content?: VSBuffer | VSBufferReadable, _options?: ICreateFileOptions): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }642createFolder(_resource: URI): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }643644onDidChangeFileSystemProviderRegistrations = Event.None;645646private providers = new Map<string, IFileSystemProvider>();647648registerProvider(scheme: string, provider: IFileSystemProvider) {649this.providers.set(scheme, provider);650651return toDisposable(() => this.providers.delete(scheme));652}653654getProvider(scheme: string) {655return this.providers.get(scheme);656}657658async activateProvider(_scheme: string): Promise<void> {659this._onWillActivateFileSystemProvider.fire({ scheme: _scheme, join: () => { } });660}661async canHandleResource(resource: URI): Promise<boolean> { return this.hasProvider(resource); }662hasProvider(resource: URI): boolean { return resource.scheme === Schemas.file || this.providers.has(resource.scheme); }663listCapabilities() {664return [665{ scheme: Schemas.file, capabilities: FileSystemProviderCapabilities.FileOpenReadWriteClose },666...Iterable.map(this.providers, ([scheme, p]) => { return { scheme, capabilities: p.capabilities }; })667];668}669hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean {670if (capability === FileSystemProviderCapabilities.PathCaseSensitive && isLinux) {671return true;672}673674const provider = this.getProvider(resource.scheme);675676return !!(provider && (provider.capabilities & capability));677}678679async del(_resource: URI, _options?: { useTrash?: boolean; recursive?: boolean }): Promise<void> { }680681createWatcher(resource: URI, options: IWatchOptions): IFileSystemWatcher {682return {683onDidChange: Event.None,684dispose: () => { }685};686}687688689readonly watches: URI[] = [];690watch(_resource: URI, options: IWatchOptionsWithCorrelation): IFileSystemWatcher;691watch(_resource: URI): IDisposable;692watch(_resource: URI): IDisposable {693this.watches.push(_resource);694695return toDisposable(() => this.watches.splice(this.watches.indexOf(_resource), 1));696}697698getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; }699dispose(): void { }700701async canCreateFile(source: URI, options?: ICreateFileOptions): Promise<Error | true> { return true; }702async canMove(source: URI, target: URI, overwrite?: boolean | undefined): Promise<Error | true> { return true; }703async canCopy(source: URI, target: URI, overwrite?: boolean | undefined): Promise<Error | true> { return true; }704async canDelete(resource: URI, options?: { useTrash?: boolean | undefined; recursive?: boolean | undefined } | undefined): Promise<Error | true> { return true; }705}706707/**708* TestFileService with in-memory file storage.709* Use this when your test needs to write files and read them back.710*/711export class InMemoryTestFileService extends TestFileService {712713private files = new ResourceMap<VSBuffer>();714715override clearTracking(): void {716super.clearTracking();717this.files.clear();718}719720override async readFile(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileContent> {721if (this.readShouldThrowError) {722throw this.readShouldThrowError;723}724725this.lastReadFileUri = resource;726this.readOperations.push({ resource });727728// Check if we have content in our in-memory store729const content = this.files.get(resource);730if (content) {731return {732...createFileStat(resource, this.readonly),733value: content734};735}736737return {738...createFileStat(resource, this.readonly),739value: VSBuffer.fromString(this.content)740};741}742743override async writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata> {744await timeout(0);745746if (this.writeShouldThrowError) {747throw this.writeShouldThrowError;748}749750let content: VSBuffer;751if (bufferOrReadable instanceof VSBuffer) {752content = bufferOrReadable;753} else {754content = readableToBuffer(bufferOrReadable);755}756757// Store in memory and track758this.files.set(resource, content);759this.writeOperations.push({ resource, content: content.toString() });760761return createFileStat(resource, this.readonly);762}763764override async del(resource: URI, _options?: { useTrash?: boolean; recursive?: boolean }): Promise<void> {765this.files.delete(resource);766this.notExistsSet.set(resource, true);767}768769override async exists(resource: URI): Promise<boolean> {770const inMemory = this.files.has(resource);771if (inMemory) {772return true;773}774775return super.exists(resource);776}777}778779export class TestChatEntitlementService implements IChatEntitlementService {780781_serviceBrand: undefined;782783readonly organisations: undefined;784readonly isInternal = false;785readonly sku = undefined;786readonly copilotTrackingId = undefined;787788readonly onDidChangeQuotaExceeded = Event.None;789readonly onDidChangeQuotaRemaining = Event.None;790readonly quotas = {};791792update(token: CancellationToken): Promise<void> {793throw new Error('Method not implemented.');794}795796readonly onDidChangeSentiment = Event.None;797readonly sentimentObs = observableValue({}, {});798readonly sentiment = {};799800readonly onDidChangeEntitlement = Event.None;801entitlement: ChatEntitlement = ChatEntitlement.Unknown;802readonly entitlementObs = observableValue({}, ChatEntitlement.Unknown);803804readonly anonymous = false;805onDidChangeAnonymous = Event.None;806readonly anonymousObs = observableValue({}, false);807808readonly previewFeaturesDisabled = false;809}810811export class TestLifecycleService extends Disposable implements ILifecycleService {812813declare readonly _serviceBrand: undefined;814815usePhases = false;816_phase!: LifecyclePhase;817get phase(): LifecyclePhase { return this._phase; }818set phase(value: LifecyclePhase) {819this._phase = value;820if (value === LifecyclePhase.Starting) {821this.whenStarted.complete();822} else if (value === LifecyclePhase.Ready) {823this.whenReady.complete();824} else if (value === LifecyclePhase.Restored) {825this.whenRestored.complete();826} else if (value === LifecyclePhase.Eventually) {827this.whenEventually.complete();828}829}830831private readonly whenStarted = new DeferredPromise<void>();832private readonly whenReady = new DeferredPromise<void>();833private readonly whenRestored = new DeferredPromise<void>();834private readonly whenEventually = new DeferredPromise<void>();835async when(phase: LifecyclePhase): Promise<void> {836if (!this.usePhases) {837return;838}839if (phase === LifecyclePhase.Starting) {840await this.whenStarted.p;841} else if (phase === LifecyclePhase.Ready) {842await this.whenReady.p;843} else if (phase === LifecyclePhase.Restored) {844await this.whenRestored.p;845} else if (phase === LifecyclePhase.Eventually) {846await this.whenEventually.p;847}848}849850startupKind!: StartupKind;851willShutdown = false;852853private readonly _onBeforeShutdown = this._register(new Emitter<InternalBeforeShutdownEvent>());854get onBeforeShutdown(): Event<InternalBeforeShutdownEvent> { return this._onBeforeShutdown.event; }855856private readonly _onBeforeShutdownError = this._register(new Emitter<BeforeShutdownErrorEvent>());857get onBeforeShutdownError(): Event<BeforeShutdownErrorEvent> { return this._onBeforeShutdownError.event; }858859private readonly _onShutdownVeto = this._register(new Emitter<void>());860get onShutdownVeto(): Event<void> { return this._onShutdownVeto.event; }861862private readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());863get onWillShutdown(): Event<WillShutdownEvent> { return this._onWillShutdown.event; }864865private readonly _onDidShutdown = this._register(new Emitter<void>());866get onDidShutdown(): Event<void> { return this._onDidShutdown.event; }867868shutdownJoiners: Promise<void>[] = [];869870fireShutdown(reason = ShutdownReason.QUIT): void {871this.shutdownJoiners = [];872873this._onWillShutdown.fire({874join: p => {875this.shutdownJoiners.push(typeof p === 'function' ? p() : p);876},877joiners: () => [],878force: () => { /* No-Op in tests */ },879token: CancellationToken.None,880reason881});882}883884fireBeforeShutdown(event: InternalBeforeShutdownEvent): void { this._onBeforeShutdown.fire(event); }885886fireWillShutdown(event: WillShutdownEvent): void { this._onWillShutdown.fire(event); }887888async shutdown(): Promise<void> {889this.fireShutdown();890}891}892893894