Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/backup/test/electron-main/backupMainService.test.ts
3297 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 assert from 'assert';
7
import { createHash } from 'crypto';
8
import * as fs from 'fs';
9
import * as os from 'os';
10
import { Schemas } from '../../../../base/common/network.js';
11
import * as path from '../../../../base/common/path.js';
12
import * as platform from '../../../../base/common/platform.js';
13
import { isEqual } from '../../../../base/common/resources.js';
14
import { URI } from '../../../../base/common/uri.js';
15
import { Promises } from '../../../../base/node/pfs.js';
16
import { flakySuite, getRandomTestPath } from '../../../../base/test/node/testUtils.js';
17
import { BackupMainService } from '../../electron-main/backupMainService.js';
18
import { ISerializedBackupWorkspaces, ISerializedWorkspaceBackupInfo } from '../../node/backup.js';
19
import { TestConfigurationService } from '../../../configuration/test/common/testConfigurationService.js';
20
import { EnvironmentMainService } from '../../../environment/electron-main/environmentMainService.js';
21
import { OPTIONS, parseArgs } from '../../../environment/node/argv.js';
22
import { HotExitConfiguration } from '../../../files/common/files.js';
23
import { ConsoleMainLogger } from '../../../log/common/log.js';
24
import product from '../../../product/common/product.js';
25
import { IFolderBackupInfo, isFolderBackupInfo, IWorkspaceBackupInfo } from '../../common/backup.js';
26
import { IWorkspaceIdentifier } from '../../../workspace/common/workspace.js';
27
import { InMemoryTestStateMainService } from '../../../test/electron-main/workbenchTestServices.js';
28
import { LogService } from '../../../log/common/logService.js';
29
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
30
31
flakySuite('BackupMainService', () => {
32
33
function assertEqualFolderInfos(actual: IFolderBackupInfo[], expected: IFolderBackupInfo[]) {
34
const withUriAsString = (f: IFolderBackupInfo) => ({ folderUri: f.folderUri.toString(), remoteAuthority: f.remoteAuthority });
35
assert.deepStrictEqual(actual.map(withUriAsString), expected.map(withUriAsString));
36
}
37
38
function toWorkspace(path: string): IWorkspaceIdentifier {
39
return {
40
id: createHash('md5').update(sanitizePath(path)).digest('hex'), // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length
41
configPath: URI.file(path)
42
};
43
}
44
45
function toWorkspaceBackupInfo(path: string, remoteAuthority?: string): IWorkspaceBackupInfo {
46
return {
47
workspace: {
48
id: createHash('md5').update(sanitizePath(path)).digest('hex'), // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length
49
configPath: URI.file(path)
50
},
51
remoteAuthority
52
};
53
}
54
55
function toFolderBackupInfo(uri: URI, remoteAuthority?: string): IFolderBackupInfo {
56
return { folderUri: uri, remoteAuthority };
57
}
58
59
function toSerializedWorkspace(ws: IWorkspaceIdentifier): ISerializedWorkspaceBackupInfo {
60
return {
61
id: ws.id,
62
configURIPath: ws.configPath.toString()
63
};
64
}
65
66
function ensureFolderExists(uri: URI): Promise<void> {
67
if (!fs.existsSync(uri.fsPath)) {
68
fs.mkdirSync(uri.fsPath);
69
}
70
71
const backupFolder = service.toBackupPath(uri);
72
return createBackupFolder(backupFolder);
73
}
74
75
async function ensureWorkspaceExists(workspace: IWorkspaceIdentifier): Promise<IWorkspaceIdentifier> {
76
if (!fs.existsSync(workspace.configPath.fsPath)) {
77
await Promises.writeFile(workspace.configPath.fsPath, 'Hello');
78
}
79
80
const backupFolder = service.toBackupPath(workspace.id);
81
await createBackupFolder(backupFolder);
82
83
return workspace;
84
}
85
86
async function createBackupFolder(backupFolder: string): Promise<void> {
87
if (!fs.existsSync(backupFolder)) {
88
fs.mkdirSync(backupFolder);
89
fs.mkdirSync(path.join(backupFolder, Schemas.file));
90
await Promises.writeFile(path.join(backupFolder, Schemas.file, 'foo.txt'), 'Hello');
91
}
92
}
93
94
function readWorkspacesMetadata(): ISerializedBackupWorkspaces {
95
return stateMainService.getItem('backupWorkspaces') as ISerializedBackupWorkspaces;
96
}
97
98
function writeWorkspacesMetadata(data: string): void {
99
if (!data) {
100
stateMainService.removeItem('backupWorkspaces');
101
} else {
102
stateMainService.setItem('backupWorkspaces', JSON.parse(data));
103
}
104
}
105
106
function sanitizePath(p: string): string {
107
return platform.isLinux ? p : p.toLowerCase();
108
}
109
110
const fooFile = URI.file(platform.isWindows ? 'C:\\foo' : '/foo');
111
const barFile = URI.file(platform.isWindows ? 'C:\\bar' : '/bar');
112
113
let service: BackupMainService & {
114
toBackupPath(arg: URI | string): string;
115
testGetFolderHash(folder: IFolderBackupInfo): string;
116
testGetWorkspaceBackups(): IWorkspaceBackupInfo[];
117
testGetFolderBackups(): IFolderBackupInfo[];
118
};
119
let configService: TestConfigurationService;
120
let stateMainService: InMemoryTestStateMainService;
121
122
let environmentService: EnvironmentMainService;
123
let testDir: string;
124
let backupHome: string;
125
let existingTestFolder1: URI;
126
127
setup(async () => {
128
testDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupmainservice');
129
backupHome = path.join(testDir, 'Backups');
130
existingTestFolder1 = URI.file(path.join(testDir, 'folder1'));
131
132
environmentService = new EnvironmentMainService(parseArgs(process.argv, OPTIONS), { _serviceBrand: undefined, ...product });
133
134
await fs.promises.mkdir(backupHome, { recursive: true });
135
136
configService = new TestConfigurationService();
137
stateMainService = new InMemoryTestStateMainService();
138
139
service = new class TestBackupMainService extends BackupMainService {
140
constructor() {
141
super(environmentService, configService, new LogService(new ConsoleMainLogger()), stateMainService);
142
143
this.backupHome = backupHome;
144
}
145
146
toBackupPath(arg: URI | string): string {
147
const id = arg instanceof URI ? super.getFolderHash({ folderUri: arg }) : arg;
148
return path.join(this.backupHome, id);
149
}
150
151
testGetFolderHash(folder: IFolderBackupInfo): string {
152
return super.getFolderHash(folder);
153
}
154
155
testGetWorkspaceBackups(): IWorkspaceBackupInfo[] {
156
return super.getWorkspaceBackups();
157
}
158
159
testGetFolderBackups(): IFolderBackupInfo[] {
160
return super.getFolderBackups();
161
}
162
};
163
164
return service.initialize();
165
});
166
167
teardown(() => {
168
return Promises.rm(testDir);
169
});
170
171
test('service validates backup workspaces on startup and cleans up (folder workspaces)', async function () {
172
173
// 1) backup workspace path does not exist
174
service.registerFolderBackup(toFolderBackupInfo(fooFile));
175
service.registerFolderBackup(toFolderBackupInfo(barFile));
176
await service.initialize();
177
assertEqualFolderInfos(service.testGetFolderBackups(), []);
178
179
// 2) backup workspace path exists with empty contents within
180
fs.mkdirSync(service.toBackupPath(fooFile));
181
fs.mkdirSync(service.toBackupPath(barFile));
182
service.registerFolderBackup(toFolderBackupInfo(fooFile));
183
service.registerFolderBackup(toFolderBackupInfo(barFile));
184
await service.initialize();
185
assertEqualFolderInfos(service.testGetFolderBackups(), []);
186
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
187
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
188
189
// 3) backup workspace path exists with empty folders within
190
fs.mkdirSync(service.toBackupPath(fooFile));
191
fs.mkdirSync(service.toBackupPath(barFile));
192
fs.mkdirSync(path.join(service.toBackupPath(fooFile), Schemas.file));
193
fs.mkdirSync(path.join(service.toBackupPath(barFile), Schemas.untitled));
194
service.registerFolderBackup(toFolderBackupInfo(fooFile));
195
service.registerFolderBackup(toFolderBackupInfo(barFile));
196
await service.initialize();
197
assertEqualFolderInfos(service.testGetFolderBackups(), []);
198
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
199
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
200
201
// 4) backup workspace path points to a workspace that no longer exists
202
// so it should convert the backup worspace to an empty workspace backup
203
const fileBackups = path.join(service.toBackupPath(fooFile), Schemas.file);
204
fs.mkdirSync(service.toBackupPath(fooFile));
205
fs.mkdirSync(service.toBackupPath(barFile));
206
fs.mkdirSync(fileBackups);
207
service.registerFolderBackup(toFolderBackupInfo(fooFile));
208
assert.strictEqual(service.testGetFolderBackups().length, 1);
209
assert.strictEqual(service.getEmptyWindowBackups().length, 0);
210
fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
211
await service.initialize();
212
assert.strictEqual(service.testGetFolderBackups().length, 0);
213
assert.strictEqual(service.getEmptyWindowBackups().length, 1);
214
});
215
216
test('service validates backup workspaces on startup and cleans up (root workspaces)', async function () {
217
218
// 1) backup workspace path does not exist
219
service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));
220
service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath));
221
await service.initialize();
222
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
223
224
// 2) backup workspace path exists with empty contents within
225
fs.mkdirSync(service.toBackupPath(fooFile));
226
fs.mkdirSync(service.toBackupPath(barFile));
227
service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));
228
service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath));
229
await service.initialize();
230
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
231
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
232
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
233
234
// 3) backup workspace path exists with empty folders within
235
fs.mkdirSync(service.toBackupPath(fooFile));
236
fs.mkdirSync(service.toBackupPath(barFile));
237
fs.mkdirSync(path.join(service.toBackupPath(fooFile), Schemas.file));
238
fs.mkdirSync(path.join(service.toBackupPath(barFile), Schemas.untitled));
239
service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));
240
service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath));
241
await service.initialize();
242
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
243
assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));
244
assert.ok(!fs.existsSync(service.toBackupPath(barFile)));
245
246
// 4) backup workspace path points to a workspace that no longer exists
247
// so it should convert the backup worspace to an empty workspace backup
248
const fileBackups = path.join(service.toBackupPath(fooFile), Schemas.file);
249
fs.mkdirSync(service.toBackupPath(fooFile));
250
fs.mkdirSync(service.toBackupPath(barFile));
251
fs.mkdirSync(fileBackups);
252
service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));
253
assert.strictEqual(service.testGetWorkspaceBackups().length, 1);
254
assert.strictEqual(service.getEmptyWindowBackups().length, 0);
255
fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
256
await service.initialize();
257
assert.strictEqual(service.testGetWorkspaceBackups().length, 0);
258
assert.strictEqual(service.getEmptyWindowBackups().length, 1);
259
});
260
261
test('service supports to migrate backup data from another location', async () => {
262
const backupPathToMigrate = service.toBackupPath(fooFile);
263
fs.mkdirSync(backupPathToMigrate);
264
fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');
265
service.registerFolderBackup(toFolderBackupInfo(URI.file(backupPathToMigrate)));
266
267
const workspaceBackupPath = await service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate);
268
269
assert.ok(fs.existsSync(workspaceBackupPath));
270
assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt')));
271
assert.ok(!fs.existsSync(backupPathToMigrate));
272
273
const emptyBackups = service.getEmptyWindowBackups();
274
assert.strictEqual(0, emptyBackups.length);
275
});
276
277
test('service backup migration makes sure to preserve existing backups', async () => {
278
const backupPathToMigrate = service.toBackupPath(fooFile);
279
fs.mkdirSync(backupPathToMigrate);
280
fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');
281
service.registerFolderBackup(toFolderBackupInfo(URI.file(backupPathToMigrate)));
282
283
const backupPathToPreserve = service.toBackupPath(barFile);
284
fs.mkdirSync(backupPathToPreserve);
285
fs.writeFileSync(path.join(backupPathToPreserve, 'backup.txt'), 'Some Data');
286
service.registerFolderBackup(toFolderBackupInfo(URI.file(backupPathToPreserve)));
287
288
const workspaceBackupPath = await service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate);
289
290
assert.ok(fs.existsSync(workspaceBackupPath));
291
assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt')));
292
assert.ok(!fs.existsSync(backupPathToMigrate));
293
294
const emptyBackups = service.getEmptyWindowBackups();
295
assert.strictEqual(1, emptyBackups.length);
296
assert.strictEqual(1, fs.readdirSync(path.join(backupHome, emptyBackups[0].backupFolder)).length);
297
});
298
299
suite('loadSync', () => {
300
test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
301
assertEqualFolderInfos(service.testGetFolderBackups(), []);
302
});
303
304
test('getFolderBackupPaths() should return [] when folders in workspaces.json is absent', async () => {
305
writeWorkspacesMetadata('{}');
306
await service.initialize();
307
assertEqualFolderInfos(service.testGetFolderBackups(), []);
308
});
309
310
test('getFolderBackupPaths() should return [] when folders in workspaces.json is not a string array', async () => {
311
writeWorkspacesMetadata('{"folders":{}}');
312
await service.initialize();
313
assertEqualFolderInfos(service.testGetFolderBackups(), []);
314
writeWorkspacesMetadata('{"folders":{"foo": ["bar"]}}');
315
await service.initialize();
316
assertEqualFolderInfos(service.testGetFolderBackups(), []);
317
writeWorkspacesMetadata('{"folders":{"foo": []}}');
318
await service.initialize();
319
assertEqualFolderInfos(service.testGetFolderBackups(), []);
320
writeWorkspacesMetadata('{"folders":{"foo": "bar"}}');
321
await service.initialize();
322
assertEqualFolderInfos(service.testGetFolderBackups(), []);
323
writeWorkspacesMetadata('{"folders":"foo"}');
324
await service.initialize();
325
assertEqualFolderInfos(service.testGetFolderBackups(), []);
326
writeWorkspacesMetadata('{"folders":1}');
327
await service.initialize();
328
assertEqualFolderInfos(service.testGetFolderBackups(), []);
329
});
330
331
test('getFolderBackupPaths() should return [] when files.hotExit = "onExitAndWindowClose"', async () => {
332
const fi = toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase()));
333
service.registerFolderBackup(fi);
334
assertEqualFolderInfos(service.testGetFolderBackups(), [fi]);
335
configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
336
await service.initialize();
337
assertEqualFolderInfos(service.testGetFolderBackups(), []);
338
});
339
340
test('getWorkspaceBackups() should return [] when workspaces.json doesn\'t exist', () => {
341
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
342
});
343
344
test('getWorkspaceBackups() should return [] when folderWorkspaces in workspaces.json is absent', async () => {
345
writeWorkspacesMetadata('{}');
346
await service.initialize();
347
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
348
});
349
350
test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array', async () => {
351
writeWorkspacesMetadata('{"rootWorkspaces":{}}');
352
await service.initialize();
353
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
354
writeWorkspacesMetadata('{"rootWorkspaces":{"foo": ["bar"]}}');
355
await service.initialize();
356
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
357
writeWorkspacesMetadata('{"rootWorkspaces":{"foo": []}}');
358
await service.initialize();
359
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
360
writeWorkspacesMetadata('{"rootWorkspaces":{"foo": "bar"}}');
361
await service.initialize();
362
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
363
writeWorkspacesMetadata('{"rootWorkspaces":"foo"}');
364
await service.initialize();
365
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
366
writeWorkspacesMetadata('{"rootWorkspaces":1}');
367
await service.initialize();
368
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
369
});
370
371
test('getWorkspaceBackups() should return [] when workspaces in workspaces.json is not a object array', async () => {
372
writeWorkspacesMetadata('{"workspaces":{}}');
373
await service.initialize();
374
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
375
writeWorkspacesMetadata('{"workspaces":{"foo": ["bar"]}}');
376
await service.initialize();
377
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
378
writeWorkspacesMetadata('{"workspaces":{"foo": []}}');
379
await service.initialize();
380
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
381
writeWorkspacesMetadata('{"workspaces":{"foo": "bar"}}');
382
await service.initialize();
383
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
384
writeWorkspacesMetadata('{"workspaces":"foo"}');
385
await service.initialize();
386
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
387
writeWorkspacesMetadata('{"workspaces":1}');
388
await service.initialize();
389
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
390
});
391
392
test('getWorkspaceBackups() should return [] when files.hotExit = "onExitAndWindowClose"', async () => {
393
const upperFooPath = fooFile.fsPath.toUpperCase();
394
service.registerWorkspaceBackup(toWorkspaceBackupInfo(upperFooPath));
395
assert.strictEqual(service.testGetWorkspaceBackups().length, 1);
396
assert.deepStrictEqual(service.testGetWorkspaceBackups().map(r => r.workspace.configPath.toString()), [URI.file(upperFooPath).toString()]);
397
configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
398
await service.initialize();
399
assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);
400
});
401
402
test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
403
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
404
});
405
406
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', async () => {
407
writeWorkspacesMetadata('{}');
408
await service.initialize();
409
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
410
});
411
412
test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', async function () {
413
writeWorkspacesMetadata('{"emptyWorkspaces":{}}');
414
await service.initialize();
415
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
416
writeWorkspacesMetadata('{"emptyWorkspaces":{"foo": ["bar"]}}');
417
await service.initialize();
418
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
419
writeWorkspacesMetadata('{"emptyWorkspaces":{"foo": []}}');
420
await service.initialize();
421
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
422
writeWorkspacesMetadata('{"emptyWorkspaces":{"foo": "bar"}}');
423
await service.initialize();
424
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
425
writeWorkspacesMetadata('{"emptyWorkspaces":"foo"}');
426
await service.initialize();
427
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
428
writeWorkspacesMetadata('{"emptyWorkspaces":1}');
429
await service.initialize();
430
assert.deepStrictEqual(service.getEmptyWindowBackups(), []);
431
});
432
});
433
434
suite('dedupeFolderWorkspaces', () => {
435
test('should ignore duplicates (folder workspace)', async () => {
436
437
await ensureFolderExists(existingTestFolder1);
438
439
const workspacesJson: ISerializedBackupWorkspaces = {
440
workspaces: [],
441
folders: [{ folderUri: existingTestFolder1.toString() }, { folderUri: existingTestFolder1.toString() }],
442
emptyWindows: []
443
};
444
writeWorkspacesMetadata(JSON.stringify(workspacesJson));
445
await service.initialize();
446
447
const json = readWorkspacesMetadata();
448
assert.deepStrictEqual(json.folders, [{ folderUri: existingTestFolder1.toString() }]);
449
});
450
451
test('should ignore duplicates on Windows and Mac (folder workspace)', async () => {
452
453
await ensureFolderExists(existingTestFolder1);
454
455
const workspacesJson: ISerializedBackupWorkspaces = {
456
workspaces: [],
457
folders: [{ folderUri: existingTestFolder1.toString() }, { folderUri: existingTestFolder1.toString().toLowerCase() }],
458
emptyWindows: []
459
};
460
writeWorkspacesMetadata(JSON.stringify(workspacesJson));
461
await service.initialize();
462
const json = readWorkspacesMetadata();
463
assert.deepStrictEqual(json.folders, [{ folderUri: existingTestFolder1.toString() }]);
464
});
465
466
test('should ignore duplicates on Windows and Mac (root workspace)', async () => {
467
const workspacePath = path.join(testDir, 'Foo.code-workspace');
468
const workspacePath1 = path.join(testDir, 'FOO.code-workspace');
469
const workspacePath2 = path.join(testDir, 'foo.code-workspace');
470
471
const workspace1 = await ensureWorkspaceExists(toWorkspace(workspacePath));
472
const workspace2 = await ensureWorkspaceExists(toWorkspace(workspacePath1));
473
const workspace3 = await ensureWorkspaceExists(toWorkspace(workspacePath2));
474
475
const workspacesJson: ISerializedBackupWorkspaces = {
476
workspaces: [workspace1, workspace2, workspace3].map(toSerializedWorkspace),
477
folders: [],
478
emptyWindows: []
479
};
480
writeWorkspacesMetadata(JSON.stringify(workspacesJson));
481
await service.initialize();
482
483
const json = readWorkspacesMetadata();
484
assert.strictEqual(json.workspaces.length, platform.isLinux ? 3 : 1);
485
if (platform.isLinux) {
486
assert.deepStrictEqual(json.workspaces.map(r => r.configURIPath), [URI.file(workspacePath).toString(), URI.file(workspacePath1).toString(), URI.file(workspacePath2).toString()]);
487
} else {
488
assert.deepStrictEqual(json.workspaces.map(r => r.configURIPath), [URI.file(workspacePath).toString()], 'should return the first duplicated entry');
489
}
490
});
491
});
492
493
suite('registerWindowForBackups', () => {
494
test('should persist paths to workspaces.json (folder workspace)', async () => {
495
service.registerFolderBackup(toFolderBackupInfo(fooFile));
496
service.registerFolderBackup(toFolderBackupInfo(barFile));
497
assertEqualFolderInfos(service.testGetFolderBackups(), [toFolderBackupInfo(fooFile), toFolderBackupInfo(barFile)]);
498
499
const json = readWorkspacesMetadata();
500
assert.deepStrictEqual(json.folders, [{ folderUri: fooFile.toString() }, { folderUri: barFile.toString() }]);
501
});
502
503
test('should persist paths to workspaces.json (root workspace)', async () => {
504
const ws1 = toWorkspaceBackupInfo(fooFile.fsPath);
505
service.registerWorkspaceBackup(ws1);
506
const ws2 = toWorkspaceBackupInfo(barFile.fsPath);
507
service.registerWorkspaceBackup(ws2);
508
509
assert.deepStrictEqual(service.testGetWorkspaceBackups().map(b => b.workspace.configPath.toString()), [fooFile.toString(), barFile.toString()]);
510
assert.strictEqual(ws1.workspace.id, service.testGetWorkspaceBackups()[0].workspace.id);
511
assert.strictEqual(ws2.workspace.id, service.testGetWorkspaceBackups()[1].workspace.id);
512
513
const json = readWorkspacesMetadata();
514
assert.deepStrictEqual(json.workspaces.map(b => b.configURIPath), [fooFile.toString(), barFile.toString()]);
515
assert.strictEqual(ws1.workspace.id, json.workspaces[0].id);
516
assert.strictEqual(ws2.workspace.id, json.workspaces[1].id);
517
});
518
});
519
520
test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (folder workspace)', async () => {
521
service.registerFolderBackup(toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase())));
522
assertEqualFolderInfos(service.testGetFolderBackups(), [toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase()))]);
523
524
const json = readWorkspacesMetadata();
525
assert.deepStrictEqual(json.folders, [{ folderUri: URI.file(fooFile.fsPath.toUpperCase()).toString() }]);
526
});
527
528
test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (root workspace)', async () => {
529
const upperFooPath = fooFile.fsPath.toUpperCase();
530
service.registerWorkspaceBackup(toWorkspaceBackupInfo(upperFooPath));
531
assert.deepStrictEqual(service.testGetWorkspaceBackups().map(b => b.workspace.configPath.toString()), [URI.file(upperFooPath).toString()]);
532
533
const json = readWorkspacesMetadata();
534
assert.deepStrictEqual(json.workspaces.map(b => b.configURIPath), [URI.file(upperFooPath).toString()]);
535
});
536
537
suite('getWorkspaceHash', () => {
538
(platform.isLinux ? test.skip : test)('should ignore case on Windows and Mac', () => {
539
const assertFolderHash = (uri1: URI, uri2: URI) => {
540
assert.strictEqual(service.testGetFolderHash(toFolderBackupInfo(uri1)), service.testGetFolderHash(toFolderBackupInfo(uri2)));
541
};
542
543
if (platform.isMacintosh) {
544
assertFolderHash(URI.file('/foo'), URI.file('/FOO'));
545
}
546
547
if (platform.isWindows) {
548
assertFolderHash(URI.file('c:\\foo'), URI.file('C:\\FOO'));
549
}
550
});
551
});
552
553
suite('mixed path casing', () => {
554
test('should handle case insensitive paths properly (registerWindowForBackupsSync) (folder workspace)', () => {
555
service.registerFolderBackup(toFolderBackupInfo(fooFile));
556
service.registerFolderBackup(toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase())));
557
558
if (platform.isLinux) {
559
assert.strictEqual(service.testGetFolderBackups().length, 2);
560
} else {
561
assert.strictEqual(service.testGetFolderBackups().length, 1);
562
}
563
});
564
565
test('should handle case insensitive paths properly (registerWindowForBackupsSync) (root workspace)', () => {
566
service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));
567
service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath.toUpperCase()));
568
569
if (platform.isLinux) {
570
assert.strictEqual(service.testGetWorkspaceBackups().length, 2);
571
} else {
572
assert.strictEqual(service.testGetWorkspaceBackups().length, 1);
573
}
574
});
575
});
576
577
suite('getDirtyWorkspaces', () => {
578
test('should report if a workspace or folder has backups', async () => {
579
const folderBackupPath = service.registerFolderBackup(toFolderBackupInfo(fooFile));
580
581
const backupWorkspaceInfo = toWorkspaceBackupInfo(fooFile.fsPath);
582
const workspaceBackupPath = service.registerWorkspaceBackup(backupWorkspaceInfo);
583
584
assert.strictEqual(((await service.getDirtyWorkspaces()).length), 0);
585
586
try {
587
await fs.promises.mkdir(path.join(folderBackupPath, Schemas.file), { recursive: true });
588
await fs.promises.mkdir(path.join(workspaceBackupPath, Schemas.untitled), { recursive: true });
589
} catch (error) {
590
// ignore - folder might exist already
591
}
592
593
assert.strictEqual(((await service.getDirtyWorkspaces()).length), 0);
594
595
fs.writeFileSync(path.join(folderBackupPath, Schemas.file, '594a4a9d82a277a899d4713a5b08f504'), '');
596
fs.writeFileSync(path.join(workspaceBackupPath, Schemas.untitled, '594a4a9d82a277a899d4713a5b08f504'), '');
597
598
const dirtyWorkspaces = await service.getDirtyWorkspaces();
599
assert.strictEqual(dirtyWorkspaces.length, 2);
600
601
let found = 0;
602
for (const dirtyWorkpspace of dirtyWorkspaces) {
603
if (isFolderBackupInfo(dirtyWorkpspace)) {
604
if (isEqual(fooFile, dirtyWorkpspace.folderUri)) {
605
found++;
606
}
607
} else {
608
if (isEqual(backupWorkspaceInfo.workspace.configPath, dirtyWorkpspace.workspace.configPath)) {
609
found++;
610
}
611
}
612
}
613
614
assert.strictEqual(found, 2);
615
});
616
});
617
618
ensureNoDisposablesAreLeakedInTestSuite();
619
});
620
621