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