backupMainService.test.ts 25.7 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import * as assert from 'assert';
import * as platform from 'vs/base/common/platform';
D
Daniel Imms 已提交
10
import fs = require('fs');
11 12 13 14
import os = require('os');
import path = require('path');
import pfs = require('vs/base/node/pfs');
import Uri from 'vs/base/common/uri';
15 16
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
D
Daniel Imms 已提交
17
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
18
import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup';
19 20
import { HotExitConfiguration } from 'vs/platform/files/common/files';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
21
import { ConsoleLogMainService } from 'vs/platform/log/common/log';
B
Benjamin Pasero 已提交
22 23
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { createHash } from 'crypto';
B
Benjamin Pasero 已提交
24
import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices';
25
import { Schemas } from 'vs/base/common/network';
26

27
suite('BackupMainService', () => {
B
Benjamin Pasero 已提交
28
	const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupservice');
29 30
	const backupHome = path.join(parentDir, 'Backups');
	const backupWorkspacesPath = path.join(backupHome, 'workspaces.json');
31

32
	const environmentService = new EnvironmentService(parseArgs(process.argv), process.execPath);
33

34
	class TestBackupMainService extends BackupMainService {
35

36
		constructor(backupHome: string, backupWorkspacesPath: string, configService: TestConfigurationService) {
S
Sandeep Somavarapu 已提交
37
			super(environmentService, configService, new ConsoleLogMainService());
38

39 40
			this.backupHome = backupHome;
			this.workspacesJsonPath = backupWorkspacesPath;
41

42 43 44
			// Force a reload with the new paths
			this.loadSync();
		}
45

46 47 48
		public get backupsData(): IBackupWorkspacesFormat {
			return this.backups;
		}
D
Daniel Imms 已提交
49

50 51 52
		public removeBackupPathSync(workspaceIdentifier: string | IWorkspaceIdentifier, target: (string | IWorkspaceIdentifier)[]): void {
			return super.removeBackupPathSync(workspaceIdentifier, target);
		}
53

54 55 56
		public loadSync(): void {
			super.loadSync();
		}
57

58 59 60
		public dedupeBackups(backups: IBackupWorkspacesFormat): IBackupWorkspacesFormat {
			return super.dedupeBackups(backups);
		}
61

62 63 64
		public toBackupPath(workspacePath: string): string {
			return path.join(this.backupHome, super.getFolderHash(workspacePath));
		}
65

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
		public getFolderHash(folderPath: string): string {
			return super.getFolderHash(folderPath);
		}
	}

	function toWorkspace(path: string): IWorkspaceIdentifier {
		return {
			id: createHash('md5').update(sanitizePath(path)).digest('hex'),
			configPath: path
		};
	}

	function sanitizePath(p: string): string {
		return platform.isLinux ? p : p.toLowerCase();
	}
81 82 83 84

	const fooFile = Uri.file(platform.isWindows ? 'C:\\foo' : '/foo');
	const barFile = Uri.file(platform.isWindows ? 'C:\\bar' : '/bar');

85
	let service: TestBackupMainService;
86
	let configService: TestConfigurationService;
87

88
	setup(() => {
89 90
		configService = new TestConfigurationService();
		service = new TestBackupMainService(backupHome, backupWorkspacesPath, configService);
91 92

		// Delete any existing backups completely and then re-create it.
93 94
		return pfs.del(backupHome, os.tmpdir()).then(() => {
			return pfs.mkdirp(backupHome);
95 96 97
		});
	});

98 99
	teardown(() => {
		return pfs.del(backupHome, os.tmpdir());
100 101
	});

102
	test('service validates backup workspaces on startup and cleans up (folder workspaces)', () => {
103

104
		// 1) backup workspace path does not exist
B
Benjamin Pasero 已提交
105 106
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
107
		service.loadSync();
108
		assert.deepEqual(service.getFolderBackupPaths(), []);
109 110 111 112

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
B
Benjamin Pasero 已提交
113 114
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
115
		service.loadSync();
116
		assert.deepEqual(service.getFolderBackupPaths(), []);
B
Benjamin Pasero 已提交
117 118
		assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath)));
119 120 121 122

		// 3) backup workspace path exists with empty folders within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
123 124
		fs.mkdirSync(path.join(service.toBackupPath(fooFile.fsPath), Schemas.file));
		fs.mkdirSync(path.join(service.toBackupPath(barFile.fsPath), Schemas.untitled));
B
Benjamin Pasero 已提交
125 126
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
127
		service.loadSync();
128
		assert.deepEqual(service.getFolderBackupPaths(), []);
B
Benjamin Pasero 已提交
129 130
		assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath)));
131

132 133
		// 4) backup workspace path points to a workspace that no longer exists
		// so it should convert the backup worspace to an empty workspace backup
134
		const fileBackups = path.join(service.toBackupPath(fooFile.fsPath), Schemas.file);
135 136 137
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
		fs.mkdirSync(fileBackups);
B
Benjamin Pasero 已提交
138
		service.registerFolderBackupSync(fooFile.fsPath);
139 140 141 142 143 144 145 146
		assert.equal(service.getFolderBackupPaths().length, 1);
		assert.equal(service.getEmptyWindowBackupPaths().length, 0);
		fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
		service.loadSync();
		assert.equal(service.getFolderBackupPaths().length, 0);
		assert.equal(service.getEmptyWindowBackupPaths().length, 1);
	});

147
	test('service validates backup workspaces on startup and cleans up (root workspaces)', () => {
148 149

		// 1) backup workspace path does not exist
150 151
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
152
		service.loadSync();
153
		assert.deepEqual(service.getWorkspaceBackups(), []);
154 155 156 157

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
158 159
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
160
		service.loadSync();
161
		assert.deepEqual(service.getWorkspaceBackups(), []);
B
Benjamin Pasero 已提交
162 163
		assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath)));
164 165 166 167

		// 3) backup workspace path exists with empty folders within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
168 169
		fs.mkdirSync(path.join(service.toBackupPath(fooFile.fsPath), Schemas.file));
		fs.mkdirSync(path.join(service.toBackupPath(barFile.fsPath), Schemas.untitled));
170 171
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
172
		service.loadSync();
173
		assert.deepEqual(service.getWorkspaceBackups(), []);
B
Benjamin Pasero 已提交
174 175
		assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath)));
176 177 178

		// 4) backup workspace path points to a workspace that no longer exists
		// so it should convert the backup worspace to an empty workspace backup
179
		const fileBackups = path.join(service.toBackupPath(fooFile.fsPath), Schemas.file);
180 181 182
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
		fs.mkdirSync(fileBackups);
183 184
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		assert.equal(service.getWorkspaceBackups().length, 1);
185
		assert.equal(service.getEmptyWindowBackupPaths().length, 0);
186 187
		fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
		service.loadSync();
188
		assert.equal(service.getWorkspaceBackups().length, 0);
189
		assert.equal(service.getEmptyWindowBackupPaths().length, 1);
190 191
	});

192
	test('service supports to migrate backup data from another location', () => {
193 194 195 196 197 198 199 200 201 202 203
		const backupPathToMigrate = service.toBackupPath(fooFile.fsPath);
		fs.mkdirSync(backupPathToMigrate);
		fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');
		service.registerFolderBackupSync(backupPathToMigrate);

		const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath), backupPathToMigrate);

		assert.ok(fs.existsSync(workspaceBackupPath));
		assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt')));
		assert.ok(!fs.existsSync(backupPathToMigrate));

204 205 206 207
		const emptyBackups = service.getEmptyWindowBackupPaths();
		assert.equal(0, emptyBackups.length);
	});

208
	test('service backup migration makes sure to preserve existing backups', () => {
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
		const backupPathToMigrate = service.toBackupPath(fooFile.fsPath);
		fs.mkdirSync(backupPathToMigrate);
		fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');
		service.registerFolderBackupSync(backupPathToMigrate);

		const backupPathToPreserve = service.toBackupPath(barFile.fsPath);
		fs.mkdirSync(backupPathToPreserve);
		fs.writeFileSync(path.join(backupPathToPreserve, 'backup.txt'), 'Some Data');
		service.registerFolderBackupSync(backupPathToPreserve);

		const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath), backupPathToMigrate);

		assert.ok(fs.existsSync(workspaceBackupPath));
		assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt')));
		assert.ok(!fs.existsSync(backupPathToMigrate));

		const emptyBackups = service.getEmptyWindowBackupPaths();
		assert.equal(1, emptyBackups.length);
		assert.equal(1, fs.readdirSync(path.join(backupHome, emptyBackups[0])).length);
228 229
	});

230
	suite('loadSync', () => {
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
		test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
			assert.deepEqual(service.getFolderBackupPaths(), []);
		});

		test('getFolderBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => {
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
		});

		test('getFolderBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => {
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
		});

		test('getFolderBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', () => {
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{}}');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": ["bar"]}}');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": []}}');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": "bar"}}');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":"foo"}');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":1}');
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
		});

		test('getFolderBackupPaths() should return [] when files.hotExit = "onExitAndWindowClose"', () => {
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
			assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath.toUpperCase()]);
			configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
			service.loadSync();
			assert.deepEqual(service.getFolderBackupPaths(), []);
		});

282 283
		test('getWorkspaceBackups() should return [] when workspaces.json doesn\'t exist', () => {
			assert.deepEqual(service.getWorkspaceBackups(), []);
284
		});
D
Daniel Imms 已提交
285

286
		test('getWorkspaceBackups() should return [] when workspaces.json is not properly formed JSON', () => {
287 288
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
289
			assert.deepEqual(service.getWorkspaceBackups(), []);
290 291
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
292
			assert.deepEqual(service.getWorkspaceBackups(), []);
293 294
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
295
			assert.deepEqual(service.getWorkspaceBackups(), []);
296
		});
D
Daniel Imms 已提交
297

298
		test('getWorkspaceBackups() should return [] when folderWorkspaces in workspaces.json is absent', () => {
299 300
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
301
			assert.deepEqual(service.getWorkspaceBackups(), []);
302 303
		});

304
		test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array', () => {
305
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{}}');
306
			service.loadSync();
307
			assert.deepEqual(service.getWorkspaceBackups(), []);
308
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": ["bar"]}}');
309
			service.loadSync();
310
			assert.deepEqual(service.getWorkspaceBackups(), []);
311
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": []}}');
312
			service.loadSync();
313
			assert.deepEqual(service.getWorkspaceBackups(), []);
314
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": "bar"}}');
315
			service.loadSync();
316
			assert.deepEqual(service.getWorkspaceBackups(), []);
317
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":"foo"}');
318
			service.loadSync();
319
			assert.deepEqual(service.getWorkspaceBackups(), []);
320
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":1}');
321
			service.loadSync();
322
			assert.deepEqual(service.getWorkspaceBackups(), []);
323
		});
D
Daniel Imms 已提交
324

325 326 327 328
		test('getWorkspaceBackups() should return [] when files.hotExit = "onExitAndWindowClose"', () => {
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
			assert.equal(service.getWorkspaceBackups().length, 1);
			assert.deepEqual(service.getWorkspaceBackups().map(r => r.configPath), [fooFile.fsPath.toUpperCase()]);
329 330
			configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
			service.loadSync();
331
			assert.deepEqual(service.getWorkspaceBackups(), []);
332 333
		});

334
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
335
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
336 337
		});

338
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => {
339 340
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
341
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
342 343
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
344
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
345 346
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
347
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
348 349
		});

350
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => {
351 352
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
353
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
354 355
		});

C
Christof Marti 已提交
356
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', function () {
C
Christof Marti 已提交
357
			this.timeout(5000);
358 359
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{}}');
			service.loadSync();
360
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
361 362
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": ["bar"]}}');
			service.loadSync();
363
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
364 365
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": []}}');
			service.loadSync();
366
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
367 368
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": "bar"}}');
			service.loadSync();
369
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
370 371
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":"foo"}');
			service.loadSync();
372
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
373 374
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":1}');
			service.loadSync();
375
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
376
		});
D
Daniel Imms 已提交
377
	});
378

379
	suite('dedupeFolderWorkspaces', () => {
380
		test('should ignore duplicates on Windows and Mac (folder workspace)', () => {
381 382 383 384 385
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}

D
Daniel Imms 已提交
386
			const backups: IBackupWorkspacesFormat = {
387
				rootWorkspaces: [],
388
				folderWorkspaces: platform.isWindows ? ['c:\\FOO', 'C:\\FOO', 'c:\\foo'] : ['/FOO', '/foo'],
D
Daniel Imms 已提交
389 390 391
				emptyWorkspaces: []
			};

392
			service.dedupeBackups(backups);
393

394
			assert.equal(backups.folderWorkspaces.length, 1);
395
			if (platform.isWindows) {
396
				assert.deepEqual(backups.folderWorkspaces, ['c:\\FOO'], 'should return the first duplicated entry');
D
Daniel Imms 已提交
397
			} else {
398
				assert.deepEqual(backups.folderWorkspaces, ['/FOO'], 'should return the first duplicated entry');
399 400
			}
		});
401 402 403 404 405 406 407 408

		test('should ignore duplicates on Windows and Mac (root workspace)', () => {
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}

			const backups: IBackupWorkspacesFormat = {
409
				rootWorkspaces: platform.isWindows ? [toWorkspace('c:\\FOO'), toWorkspace('C:\\FOO'), toWorkspace('c:\\foo')] : [toWorkspace('/FOO'), toWorkspace('/foo')],
410 411 412 413 414 415 416 417
				folderWorkspaces: [],
				emptyWorkspaces: []
			};

			service.dedupeBackups(backups);

			assert.equal(backups.rootWorkspaces.length, 1);
			if (platform.isWindows) {
418
				assert.deepEqual(backups.rootWorkspaces.map(r => r.configPath), ['c:\\FOO'], 'should return the first duplicated entry');
419
			} else {
420
				assert.deepEqual(backups.rootWorkspaces.map(r => r.configPath), ['/FOO'], 'should return the first duplicated entry');
421 422
			}
		});
D
Daniel Imms 已提交
423 424
	});

425
	suite('registerWindowForBackups', () => {
426
		test('should persist paths to workspaces.json (folder workspace)', () => {
B
Benjamin Pasero 已提交
427 428
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(barFile.fsPath);
429
			assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath, barFile.fsPath]);
430
			return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
431 432
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath, barFile.fsPath]);
433 434
			});
		});
D
Daniel Imms 已提交
435

436
		test('should persist paths to workspaces.json (root workspace)', () => {
437 438 439 440 441 442 443 444 445
			const ws1 = toWorkspace(fooFile.fsPath);
			service.registerWorkspaceBackupSync(ws1);
			const ws2 = toWorkspace(barFile.fsPath);
			service.registerWorkspaceBackupSync(ws2);

			assert.deepEqual(service.getWorkspaceBackups().map(b => b.configPath), [fooFile.fsPath, barFile.fsPath]);
			assert.equal(ws1.id, service.getWorkspaceBackups()[0].id);
			assert.equal(ws2.id, service.getWorkspaceBackups()[1].id);

446
			return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
447
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
448 449 450 451

				assert.deepEqual(json.rootWorkspaces.map(b => b.configPath), [fooFile.fsPath, barFile.fsPath]);
				assert.equal(ws1.id, json.rootWorkspaces[0].id);
				assert.equal(ws2.id, json.rootWorkspaces[1].id);
452 453 454
			});
		});

455
		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)', () => {
B
Benjamin Pasero 已提交
456
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
457
			assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath.toUpperCase()]);
458
			return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
D
Daniel Imms 已提交
459 460 461 462
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath.toUpperCase()]);
			});
		});
463

464
		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)', () => {
465 466
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
			assert.deepEqual(service.getWorkspaceBackups().map(b => b.configPath), [fooFile.fsPath.toUpperCase()]);
467
			return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
468
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
469
				assert.deepEqual(json.rootWorkspaces.map(b => b.configPath), [fooFile.fsPath.toUpperCase()]);
470 471
			});
		});
472 473
	});

474
	suite('removeBackupPathSync', () => {
475
		test('should remove folder workspaces from workspaces.json (folder workspace)', () => {
B
Benjamin Pasero 已提交
476 477
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(barFile.fsPath);
478
			service.removeBackupPathSync(fooFile.fsPath, service.backupsData.folderWorkspaces);
479
			return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
480 481
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [barFile.fsPath]);
482
				service.removeBackupPathSync(barFile.fsPath, service.backupsData.folderWorkspaces);
483
				return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
484 485 486
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.folderWorkspaces, []);
				});
487 488
			});
		});
489

490
		test('should remove folder workspaces from workspaces.json (root workspace)', () => {
491 492 493 494 495
			const ws1 = toWorkspace(fooFile.fsPath);
			service.registerWorkspaceBackupSync(ws1);
			const ws2 = toWorkspace(barFile.fsPath);
			service.registerWorkspaceBackupSync(ws2);
			service.removeBackupPathSync(ws1, service.backupsData.rootWorkspaces);
496
			return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
497
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
498 499
				assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [barFile.fsPath]);
				service.removeBackupPathSync(ws2, service.backupsData.rootWorkspaces);
500
				return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
501 502 503 504 505 506
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.rootWorkspaces, []);
				});
			});
		});

507
		test('should remove empty workspaces from workspaces.json', () => {
B
Benjamin Pasero 已提交
508 509
			service.registerEmptyWindowBackupSync('foo');
			service.registerEmptyWindowBackupSync('bar');
510
			service.removeBackupPathSync('foo', service.backupsData.emptyWorkspaces);
511
			return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
D
Daniel Imms 已提交
512 513
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.emptyWorkspaces, ['bar']);
514
				service.removeBackupPathSync('bar', service.backupsData.emptyWorkspaces);
515
				return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
D
Daniel Imms 已提交
516 517 518 519 520 521
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.emptyWorkspaces, []);
				});
			});
		});

522
		test('should fail gracefully when removing a path that doesn\'t exist', () => {
523
			const workspacesJson: IBackupWorkspacesFormat = { rootWorkspaces: [], folderWorkspaces: [fooFile.fsPath], emptyWorkspaces: [] };
524
			return pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => {
525 526
				service.removeBackupPathSync(barFile.fsPath, service.backupsData.folderWorkspaces);
				service.removeBackupPathSync('test', service.backupsData.emptyWorkspaces);
527
				return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
528 529 530 531 532
					const json = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath]);
				});
			});
		});
533
	});
D
Daniel Imms 已提交
534

535
	suite('getWorkspaceHash', () => {
D
Daniel Imms 已提交
536
		test('should perform an md5 hash on the path', () => {
537
			assert.equal(service.getFolderHash('/foo'), '1effb2475fcfba4f9e8b8a1dbc8f3caf');
D
Daniel Imms 已提交
538 539
		});

540 541 542 543 544
		test('should ignore case on Windows and Mac', () => {
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}
D
Daniel Imms 已提交
545

546
			if (platform.isMacintosh) {
547
				assert.equal(service.getFolderHash('/foo'), service.getFolderHash('/FOO'));
548
			}
D
Daniel Imms 已提交
549

550
			if (platform.isWindows) {
551
				assert.equal(service.getFolderHash('c:\\foo'), service.getFolderHash('C:\\FOO'));
552 553
			}
		});
D
Daniel Imms 已提交
554
	});
555

556
	suite('mixed path casing', () => {
557
		test('should handle case insensitive paths properly (registerWindowForBackupsSync) (folder workspace)', () => {
B
Benjamin Pasero 已提交
558 559
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
560

561 562 563 564 565 566 567
			if (platform.isLinux) {
				assert.equal(service.getFolderBackupPaths().length, 2);
			} else {
				assert.equal(service.getFolderBackupPaths().length, 1);
			}
		});

568
		test('should handle case insensitive paths properly (registerWindowForBackupsSync) (root workspace)', () => {
569 570
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
571

572
			if (platform.isLinux) {
573
				assert.equal(service.getWorkspaceBackups().length, 2);
574
			} else {
575
				assert.equal(service.getWorkspaceBackups().length, 1);
576 577 578
			}
		});

579
		test('should handle case insensitive paths properly (removeBackupPathSync) (folder workspace)', () => {
580 581

			// same case
B
Benjamin Pasero 已提交
582
			service.registerFolderBackupSync(fooFile.fsPath);
583 584
			service.removeBackupPathSync(fooFile.fsPath, service.backupsData.folderWorkspaces);
			assert.equal(service.getFolderBackupPaths().length, 0);
585 586

			// mixed case
B
Benjamin Pasero 已提交
587
			service.registerFolderBackupSync(fooFile.fsPath);
588 589 590 591 592 593 594 595
			service.removeBackupPathSync(fooFile.fsPath.toUpperCase(), service.backupsData.folderWorkspaces);

			if (platform.isLinux) {
				assert.equal(service.getFolderBackupPaths().length, 1);
			} else {
				assert.equal(service.getFolderBackupPaths().length, 0);
			}
		});
596
	});
597
});