Path: blob/main/src/vs/workbench/test/electron-browser/workbenchTestServices.ts
5241 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 { insert } from '../../../base/common/arrays.js';6import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from '../../../base/common/buffer.js';7import { CancellationToken } from '../../../base/common/cancellation.js';8import { Event } from '../../../base/common/event.js';9import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';10import { Schemas } from '../../../base/common/network.js';11import { URI } from '../../../base/common/uri.js';12import { IModelService } from '../../../editor/common/services/model.js';13import { ModelService } from '../../../editor/common/services/modelService.js';14import { TestConfigurationService } from '../../../platform/configuration/test/common/testConfigurationService.js';15import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';16import { IFileDialogService, INativeOpenDialogOptions } from '../../../platform/dialogs/common/dialogs.js';17import { IEnvironmentService, INativeEnvironmentService } from '../../../platform/environment/common/environment.js';18import { IExtensionManagementService } from '../../../platform/extensionManagement/common/extensionManagement.js';19import { AbstractNativeExtensionTipsService } from '../../../platform/extensionManagement/common/extensionTipsService.js';20import { IExtensionRecommendationNotificationService } from '../../../platform/extensionRecommendations/common/extensionRecommendations.js';21import { IFileService, IFileSystemProvider, FileSystemProviderCapabilities, IFileReadStreamOptions, IFileWriteOptions, IFileOpenOptions, IFileDeleteOptions, IFileOverwriteOptions, IStat, FileType, IWatchOptions } from '../../../platform/files/common/files.js';22import { FileService } from '../../../platform/files/common/fileService.js';23import { InMemoryFileSystemProvider } from '../../../platform/files/common/inMemoryFilesystemProvider.js';24import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';25import { ISharedProcessService } from '../../../platform/ipc/electron-browser/services.js';26import { NullLogService } from '../../../platform/log/common/log.js';27import { INativeHostOptions, INativeHostService, IOSProperties, IOSStatistics, IToastOptions, IToastResult, PowerSaveBlockerType, SystemIdleState, ThermalState } from '../../../platform/native/common/native.js';28import { IProductService } from '../../../platform/product/common/productService.js';29import { AuthInfo, Credentials } from '../../../platform/request/common/request.js';30import { IStorageService } from '../../../platform/storage/common/storage.js';31import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js';32import { IPartsSplash } from '../../../platform/theme/common/themeService.js';33import { UriIdentityService } from '../../../platform/uriIdentity/common/uriIdentityService.js';34import { FileUserDataProvider } from '../../../platform/userData/common/fileUserDataProvider.js';35import { UserDataProfilesService } from '../../../platform/userDataProfile/common/userDataProfile.js';36import { IColorScheme, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable } from '../../../platform/window/common/window.js';37import { IWorkspaceContextService } from '../../../platform/workspace/common/workspace.js';38import { IEditorService } from '../../services/editor/common/editorService.js';39import { IFilesConfigurationService } from '../../services/filesConfiguration/common/filesConfigurationService.js';40import { ILifecycleService } from '../../services/lifecycle/common/lifecycle.js';41import { IPathService } from '../../services/path/common/pathService.js';42import { ITextEditorService } from '../../services/textfile/common/textEditorService.js';43import { ITextFileService } from '../../services/textfile/common/textfiles.js';44import { NativeTextFileService } from '../../services/textfile/electron-browser/nativeTextFileService.js';45import { IWorkingCopyIdentifier } from '../../services/workingCopy/common/workingCopy.js';46import { IWorkingCopyBackupService } from '../../services/workingCopy/common/workingCopyBackup.js';47import { IWorkingCopyService } from '../../services/workingCopy/common/workingCopyService.js';48import { NativeWorkingCopyBackupService } from '../../services/workingCopy/electron-browser/workingCopyBackupService.js';49import { workbenchInstantiationService as browserWorkbenchInstantiationService, ITestInstantiationService, TestEncodingOracle, TestEnvironmentService, TestFileDialogService, TestFilesConfigurationService, TestLifecycleService, TestTextFileService } from '../browser/workbenchTestServices.js';50import { TestContextService, TestFileService } from '../common/workbenchTestServices.js';51import { ReadableStreamEvents } from '../../../base/common/stream.js';5253export class TestSharedProcessService implements ISharedProcessService {5455declare readonly _serviceBrand: undefined;5657createRawConnection(): never { throw new Error('Not Implemented'); }58getChannel(channelName: string): any { return undefined; }59registerChannel(channelName: string, channel: any): void { }60notifyRestored(): void { }61}6263export class TestNativeHostService implements INativeHostService {6465declare readonly _serviceBrand: undefined;6667readonly windowId = -1;6869readonly onDidOpenMainWindow: Event<number> = Event.None;70readonly onDidMaximizeWindow: Event<number> = Event.None;71readonly onDidUnmaximizeWindow: Event<number> = Event.None;72readonly onDidFocusMainWindow: Event<number> = Event.None;73readonly onDidBlurMainWindow: Event<number> = Event.None;74readonly onDidFocusMainOrAuxiliaryWindow: Event<number> = Event.None;75readonly onDidBlurMainOrAuxiliaryWindow: Event<number> = Event.None;76readonly onDidSuspendOS: Event<void> = Event.None;77readonly onDidResumeOS: Event<unknown> = Event.None;78readonly onDidChangeOnBatteryPower: Event<boolean> = Event.None;79readonly onDidChangeThermalState: Event<ThermalState> = Event.None;80readonly onDidChangeSpeedLimit: Event<number> = Event.None;81readonly onWillShutdownOS: Event<void> = Event.None;82readonly onDidLockScreen: Event<void> = Event.None;83readonly onDidUnlockScreen: Event<void> = Event.None;84onDidChangeColorScheme = Event.None;85onDidChangePassword = Event.None;86readonly onDidTriggerWindowSystemContextMenu: Event<{ windowId: number; x: number; y: number }> = Event.None;87onDidChangeWindowFullScreen = Event.None;88onDidChangeWindowAlwaysOnTop = Event.None;89onDidChangeDisplay = Event.None;9091windowCount = Promise.resolve(1);92getWindowCount(): Promise<number> { return this.windowCount; }9394async getWindows(): Promise<IOpenedMainWindow[]> { return []; }95async getActiveWindowId(): Promise<number | undefined> { return undefined; }96async getActiveWindowPosition(): Promise<IRectangle | undefined> { return undefined; }97async getNativeWindowHandle(windowId: number): Promise<VSBuffer | undefined> { return undefined; }9899openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;100openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;101openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {102throw new Error('Method not implemented.');103}104105async toggleFullScreen(): Promise<void> { }106async isMaximized(): Promise<boolean> { return true; }107async isFullScreen(): Promise<boolean> { return true; }108async maximizeWindow(): Promise<void> { }109async unmaximizeWindow(): Promise<void> { }110async minimizeWindow(): Promise<void> { }111async moveWindowTop(options?: INativeHostOptions): Promise<void> { }112async isWindowAlwaysOnTop(options?: INativeHostOptions): Promise<boolean> { return false; }113async toggleWindowAlwaysOnTop(options?: INativeHostOptions): Promise<void> { }114async setWindowAlwaysOnTop(alwaysOnTop: boolean, options?: INativeHostOptions): Promise<void> { }115async getCursorScreenPoint(): Promise<{ readonly point: IPoint; readonly display: IRectangle }> { throw new Error('Method not implemented.'); }116async positionWindow(position: IRectangle, options?: INativeHostOptions): Promise<void> { }117async updateWindowControls(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise<void> { }118async updateWindowAccentColor(color: string): Promise<void> { }119async setMinimumSize(width: number | undefined, height: number | undefined): Promise<void> { }120async saveWindowSplash(value: IPartsSplash): Promise<void> { }121async setBackgroundThrottling(throttling: boolean): Promise<void> { }122async focusWindow(options?: INativeHostOptions): Promise<void> { }123async showMessageBox(options: Electron.MessageBoxOptions): Promise<Electron.MessageBoxReturnValue> { throw new Error('Method not implemented.'); }124async showSaveDialog(options: Electron.SaveDialogOptions): Promise<Electron.SaveDialogReturnValue> { throw new Error('Method not implemented.'); }125async showOpenDialog(options: Electron.OpenDialogOptions): Promise<Electron.OpenDialogReturnValue> { throw new Error('Method not implemented.'); }126async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> { }127async pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void> { }128async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> { }129async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise<void> { }130async showItemInFolder(path: string): Promise<void> { }131async setRepresentedFilename(path: string): Promise<void> { }132async isAdmin(): Promise<boolean> { return false; }133async writeElevated(source: URI, target: URI): Promise<void> { }134async isRunningUnderARM64Translation(): Promise<boolean> { return false; }135async getOSProperties(): Promise<IOSProperties> { return Object.create(null); }136async getOSStatistics(): Promise<IOSStatistics> { return Object.create(null); }137async getOSVirtualMachineHint(): Promise<number> { return 0; }138async getOSColorScheme(): Promise<IColorScheme> { return { dark: true, highContrast: false }; }139async hasWSLFeatureInstalled(): Promise<boolean> { return false; }140async getProcessId(): Promise<number> { throw new Error('Method not implemented.'); }141async killProcess(): Promise<void> { }142async setDocumentEdited(edited: boolean): Promise<void> { }143async openExternal(url: string, defaultApplication?: string): Promise<boolean> { return false; }144async updateTouchBar(): Promise<void> { }145async moveItemToTrash(): Promise<void> { }146async newWindowTab(): Promise<void> { }147async showPreviousWindowTab(): Promise<void> { }148async showNextWindowTab(): Promise<void> { }149async moveWindowTabToNewWindow(): Promise<void> { }150async mergeAllWindowTabs(): Promise<void> { }151async toggleWindowTabsBar(): Promise<void> { }152async installShellCommand(): Promise<void> { }153async uninstallShellCommand(): Promise<void> { }154async notifyReady(): Promise<void> { }155async relaunch(options?: { addArgs?: string[] | undefined; removeArgs?: string[] | undefined } | undefined): Promise<void> { }156async reload(): Promise<void> { }157async closeWindow(): Promise<void> { }158async quit(): Promise<void> { }159async exit(code: number): Promise<void> { }160async openDevTools(options?: Partial<Electron.OpenDevToolsOptions> & INativeHostOptions | undefined): Promise<void> { }161async toggleDevTools(): Promise<void> { }162async stopTracing(): Promise<void> { }163async openDevToolsWindow(url: string): Promise<void> { }164async openGPUInfoWindow(): Promise<void> { }165async openContentTracingWindow(): Promise<void> { }166async resolveProxy(url: string): Promise<string | undefined> { return undefined; }167async lookupAuthorization(authInfo: AuthInfo): Promise<Credentials | undefined> { return undefined; }168async lookupKerberosAuthorization(url: string): Promise<string | undefined> { return undefined; }169async loadCertificates(): Promise<string[]> { return []; }170async isPortFree() { return Promise.resolve(true); }171async findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise<number> { return -1; }172async readClipboardText(type?: 'selection' | 'clipboard' | undefined): Promise<string> { return ''; }173async writeClipboardText(text: string, type?: 'selection' | 'clipboard' | undefined): Promise<void> { }174async readClipboardFindText(): Promise<string> { return ''; }175async writeClipboardFindText(text: string): Promise<void> { }176async writeClipboardBuffer(format: string, buffer: VSBuffer, type?: 'selection' | 'clipboard' | undefined): Promise<void> { }177async triggerPaste(options?: INativeHostOptions): Promise<void> { }178async readImage(): Promise<Uint8Array> { return Uint8Array.from([]); }179async readClipboardBuffer(format: string): Promise<VSBuffer> { return VSBuffer.wrap(Uint8Array.from([])); }180async hasClipboard(format: string, type?: 'selection' | 'clipboard' | undefined): Promise<boolean> { return false; }181async windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise<string | undefined> { return undefined; }182async createZipFile(zipPath: URI, files: { path: string; contents: string }[]): Promise<void> { }183async profileRenderer(): Promise<any> { throw new Error(); }184async getScreenshot(rect?: IRectangle): Promise<VSBuffer | undefined> { return undefined; }185async showToast(options: IToastOptions): Promise<IToastResult> { return { supported: false, clicked: false }; }186async clearToast(id: string): Promise<void> { }187async clearToasts(): Promise<void> { }188189// Power APIs190async getSystemIdleState(idleThreshold: number): Promise<SystemIdleState> { return 'unknown'; }191async getSystemIdleTime(): Promise<number> { return 0; }192async getCurrentThermalState(): Promise<ThermalState> { return 'unknown'; }193async isOnBatteryPower(): Promise<boolean> { return false; }194async startPowerSaveBlocker(type: PowerSaveBlockerType): Promise<number> { return -1; }195async stopPowerSaveBlocker(id: number): Promise<boolean> { return false; }196async isPowerSaveBlockerStarted(id: number): Promise<boolean> { return false; }197}198199export class TestExtensionTipsService extends AbstractNativeExtensionTipsService {200201constructor(202@INativeEnvironmentService environmentService: INativeEnvironmentService,203@ITelemetryService telemetryService: ITelemetryService,204@IExtensionManagementService extensionManagementService: IExtensionManagementService,205@IStorageService storageService: IStorageService,206@INativeHostService nativeHostService: INativeHostService,207@IExtensionRecommendationNotificationService extensionRecommendationNotificationService: IExtensionRecommendationNotificationService,208@IFileService fileService: IFileService,209@IProductService productService: IProductService,210) {211super(environmentService.userHome, nativeHostService, telemetryService, extensionManagementService, storageService, extensionRecommendationNotificationService, fileService, productService);212}213}214215export function workbenchInstantiationService(overrides?: {216environmentService?: (instantiationService: IInstantiationService) => IEnvironmentService;217fileService?: (instantiationService: IInstantiationService) => IFileService;218configurationService?: (instantiationService: IInstantiationService) => TestConfigurationService;219textFileService?: (instantiationService: IInstantiationService) => ITextFileService;220pathService?: (instantiationService: IInstantiationService) => IPathService;221editorService?: (instantiationService: IInstantiationService) => IEditorService;222contextKeyService?: (instantiationService: IInstantiationService) => IContextKeyService;223textEditorService?: (instantiationService: IInstantiationService) => ITextEditorService;224}, disposables = new DisposableStore()): ITestInstantiationService {225const instantiationService = browserWorkbenchInstantiationService({226workingCopyBackupService: () => disposables.add(new TestNativeWorkingCopyBackupService()),227...overrides228}, disposables);229230instantiationService.stub(INativeHostService, new TestNativeHostService());231232return instantiationService;233}234235export class TestServiceAccessor {236constructor(237@ILifecycleService public lifecycleService: TestLifecycleService,238@ITextFileService public textFileService: TestTextFileService,239@IFilesConfigurationService public filesConfigurationService: TestFilesConfigurationService,240@IWorkspaceContextService public contextService: TestContextService,241@IModelService public modelService: ModelService,242@IFileService public fileService: TestFileService,243@INativeHostService public nativeHostService: TestNativeHostService,244@IFileDialogService public fileDialogService: TestFileDialogService,245@IWorkingCopyBackupService public workingCopyBackupService: TestNativeWorkingCopyBackupService,246@IWorkingCopyService public workingCopyService: IWorkingCopyService,247@IEditorService public editorService: IEditorService248) {249}250}251252export class TestNativeTextFileServiceWithEncodingOverrides extends NativeTextFileService {253254private _testEncoding: TestEncodingOracle | undefined;255override get encoding(): TestEncodingOracle {256if (!this._testEncoding) {257this._testEncoding = this._register(this.instantiationService.createInstance(TestEncodingOracle));258}259260return this._testEncoding;261}262}263264export class TestNativeWorkingCopyBackupService extends NativeWorkingCopyBackupService implements IDisposable {265266private backupResourceJoiners: Function[];267private discardBackupJoiners: Function[];268discardedBackups: IWorkingCopyIdentifier[];269discardedAllBackups: boolean;270private pendingBackupsArr: Promise<void>[];271272constructor() {273const environmentService = TestEnvironmentService;274const logService = new NullLogService();275const fileService = new FileService(logService);276const lifecycleService = new TestLifecycleService();277// eslint-disable-next-line local/code-no-any-casts278super(environmentService as any, fileService, logService, lifecycleService);279280const inMemoryFileSystemProvider = this._register(new InMemoryFileSystemProvider());281this._register(fileService.registerProvider(Schemas.inMemory, inMemoryFileSystemProvider));282const uriIdentityService = this._register(new UriIdentityService(fileService));283const userDataProfilesService = this._register(new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));284this._register(fileService.registerProvider(Schemas.vscodeUserData, this._register(new FileUserDataProvider(Schemas.file, inMemoryFileSystemProvider, Schemas.vscodeUserData, userDataProfilesService, uriIdentityService, logService))));285286this.backupResourceJoiners = [];287this.discardBackupJoiners = [];288this.discardedBackups = [];289this.pendingBackupsArr = [];290this.discardedAllBackups = false;291292this._register(fileService);293this._register(lifecycleService);294}295296testGetFileService(): IFileService {297return this.fileService;298}299300async waitForAllBackups(): Promise<void> {301await Promise.all(this.pendingBackupsArr);302}303304joinBackupResource(): Promise<void> {305return new Promise(resolve => this.backupResourceJoiners.push(resolve));306}307308override async backup(identifier: IWorkingCopyIdentifier, content?: VSBufferReadableStream | VSBufferReadable, versionId?: number, meta?: any, token?: CancellationToken): Promise<void> {309const p = super.backup(identifier, content, versionId, meta, token);310const removeFromPendingBackups = insert(this.pendingBackupsArr, p.then(undefined, undefined));311312try {313await p;314} finally {315removeFromPendingBackups();316}317318while (this.backupResourceJoiners.length) {319this.backupResourceJoiners.pop()!();320}321}322323joinDiscardBackup(): Promise<void> {324return new Promise(resolve => this.discardBackupJoiners.push(resolve));325}326327override async discardBackup(identifier: IWorkingCopyIdentifier): Promise<void> {328await super.discardBackup(identifier);329this.discardedBackups.push(identifier);330331while (this.discardBackupJoiners.length) {332this.discardBackupJoiners.pop()!();333}334}335336override async discardBackups(filter?: { except: IWorkingCopyIdentifier[] }): Promise<void> {337this.discardedAllBackups = true;338339return super.discardBackups(filter);340}341342async getBackupContents(identifier: IWorkingCopyIdentifier): Promise<string> {343const backupResource = this.toBackupResource(identifier);344345const fileContents = await this.fileService.readFile(backupResource);346347return fileContents.value.toString();348}349}350351export class TestIPCFileSystemProvider implements IFileSystemProvider {352353readonly capabilities = FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.PathCaseSensitive;354355readonly onDidChangeCapabilities = Event.None;356readonly onDidChangeFile = Event.None;357358async stat(resource: URI): Promise<IStat> {359const { ipcRenderer } = require('electron');360const stats = await ipcRenderer.invoke('vscode:statFile', resource.fsPath);361return {362type: stats.isDirectory ? FileType.Directory : (stats.isFile ? FileType.File : FileType.Unknown),363ctime: stats.ctimeMs,364mtime: stats.mtimeMs,365size: stats.size,366permissions: stats.isReadonly ? 1 /* FilePermission.Readonly */ : undefined367};368}369370async readFile(resource: URI): Promise<Uint8Array> {371const { ipcRenderer } = require('electron');372const result = await ipcRenderer.invoke('vscode:readFile', resource.fsPath);373return VSBuffer.wrap(result).buffer;374}375376watch(resource: URI, opts: IWatchOptions): IDisposable { return { dispose: () => { } }; }377mkdir(resource: URI): Promise<void> { throw new Error('mkdir not implemented in test provider'); }378readdir(resource: URI): Promise<[string, FileType][]> { throw new Error('readdir not implemented in test provider'); }379delete(resource: URI, opts: IFileDeleteOptions): Promise<void> { throw new Error('delete not implemented in test provider'); }380rename(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void> { throw new Error('rename not implemented in test provider'); }381writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise<void> { throw new Error('writeFile not implemented in test provider'); }382readFileStream?(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array> { throw new Error('readFileStream not implemented in test provider'); }383open?(resource: URI, opts: IFileOpenOptions): Promise<number> { throw new Error('open not implemented in test provider'); }384close?(fd: number): Promise<void> { throw new Error('close not implemented in test provider'); }385read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { throw new Error('read not implemented in test provider'); }386write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { throw new Error('write not implemented in test provider'); }387}388389390