backupMainService.test.ts 26.0 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 15
import os = require('os');
import path = require('path');
import extfs = require('vs/base/node/extfs');
import pfs = require('vs/base/node/pfs');
import Uri from 'vs/base/common/uri';
16 17
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv';
D
Daniel Imms 已提交
18
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
19
import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup';
20 21
import { HotExitConfiguration } from 'vs/platform/files/common/files';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
B
Benjamin Pasero 已提交
22 23 24
import { LogMainService } from 'vs/platform/log/common/log';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { createHash } from 'crypto';
25
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
B
Benjamin Pasero 已提交
26
import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices';
27

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

33
	const environmentService = new EnvironmentService(parseArgs(process.argv), process.execPath);
34
	const logService = new LogMainService(environmentService);
35

36
	class TestBackupMainService extends BackupMainService {
37

38
		constructor(backupHome: string, backupWorkspacesPath: string, configService: TestConfigurationService) {
39
			super(environmentService, configService, new LogMainService(environmentService), new WorkspacesMainService(environmentService, logService));
40

41 42
			this.backupHome = backupHome;
			this.workspacesJsonPath = backupWorkspacesPath;
43

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

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

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

56 57 58
		public loadSync(): void {
			super.loadSync();
		}
59

60 61 62
		public dedupeBackups(backups: IBackupWorkspacesFormat): IBackupWorkspacesFormat {
			return super.dedupeBackups(backups);
		}
63

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

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
		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();
	}
83 84 85 86

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

87
	let service: TestBackupMainService;
88
	let configService: TestConfigurationService;
89 90

	setup(done => {
91 92
		configService = new TestConfigurationService();
		service = new TestBackupMainService(backupHome, backupWorkspacesPath, configService);
93 94 95 96

		// Delete any existing backups completely and then re-create it.
		extfs.del(backupHome, os.tmpdir(), () => {
			pfs.mkdirp(backupHome).then(() => {
D
Daniel Imms 已提交
97
				done();
98 99 100 101 102 103 104 105
			});
		});
	});

	teardown(done => {
		extfs.del(backupHome, os.tmpdir(), done);
	});

106
	test('service validates backup workspaces on startup and cleans up (folder workspaces)', done => {
107

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

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
B
Benjamin Pasero 已提交
117 118
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
119
		service.loadSync();
120
		assert.deepEqual(service.getFolderBackupPaths(), []);
121 122 123 124 125 126 127 128
		assert.ok(!fs.exists(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.exists(service.toBackupPath(barFile.fsPath)));

		// 3) backup workspace path exists with empty folders within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
		fs.mkdirSync(path.join(service.toBackupPath(fooFile.fsPath), 'file'));
		fs.mkdirSync(path.join(service.toBackupPath(barFile.fsPath), 'untitled'));
B
Benjamin Pasero 已提交
129 130
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
131
		service.loadSync();
132
		assert.deepEqual(service.getFolderBackupPaths(), []);
133 134 135
		assert.ok(!fs.exists(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.exists(service.toBackupPath(barFile.fsPath)));

136 137 138 139 140 141
		// 4) backup workspace path points to a workspace that no longer exists
		// so it should convert the backup worspace to an empty workspace backup
		const fileBackups = path.join(service.toBackupPath(fooFile.fsPath), 'file');
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
		fs.mkdirSync(fileBackups);
B
Benjamin Pasero 已提交
142
		service.registerFolderBackupSync(fooFile.fsPath);
143 144 145 146 147 148 149 150 151 152 153 154 155
		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);

		done();
	});

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

		// 1) backup workspace path does not exist
156 157
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
158
		service.loadSync();
159
		assert.deepEqual(service.getWorkspaceBackups(), []);
160 161 162 163

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
164 165
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
166
		service.loadSync();
167
		assert.deepEqual(service.getWorkspaceBackups(), []);
168 169 170 171 172 173 174 175
		assert.ok(!fs.exists(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.exists(service.toBackupPath(barFile.fsPath)));

		// 3) backup workspace path exists with empty folders within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
		fs.mkdirSync(path.join(service.toBackupPath(fooFile.fsPath), 'file'));
		fs.mkdirSync(path.join(service.toBackupPath(barFile.fsPath), 'untitled'));
176 177
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
178
		service.loadSync();
179
		assert.deepEqual(service.getWorkspaceBackups(), []);
180 181 182 183 184 185 186 187 188
		assert.ok(!fs.exists(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.exists(service.toBackupPath(barFile.fsPath)));

		// 4) backup workspace path points to a workspace that no longer exists
		// so it should convert the backup worspace to an empty workspace backup
		const fileBackups = path.join(service.toBackupPath(fooFile.fsPath), 'file');
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
		fs.mkdirSync(fileBackups);
189 190
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		assert.equal(service.getWorkspaceBackups().length, 1);
191
		assert.equal(service.getEmptyWindowBackupPaths().length, 0);
192 193
		fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
		service.loadSync();
194
		assert.equal(service.getWorkspaceBackups().length, 0);
195
		assert.equal(service.getEmptyWindowBackupPaths().length, 1);
196

197 198 199
		done();
	});

200 201 202 203 204 205 206 207 208 209 210 211
	test('service supports to migrate backup data from another location', done => {
		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));

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
		const emptyBackups = service.getEmptyWindowBackupPaths();
		assert.equal(0, emptyBackups.length);

		done();
	});

	test('service backup migration makes sure to preserve existing backups', done => {
		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);

239 240 241
		done();
	});

242
	suite('loadSync', () => {
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 282 283 284 285 286 287 288 289 290 291 292 293
		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(), []);
		});

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

298
		test('getWorkspaceBackups() should return [] when workspaces.json is not properly formed JSON', () => {
299 300
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
301
			assert.deepEqual(service.getWorkspaceBackups(), []);
302 303
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
304
			assert.deepEqual(service.getWorkspaceBackups(), []);
305 306
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
307
			assert.deepEqual(service.getWorkspaceBackups(), []);
308
		});
D
Daniel Imms 已提交
309

310
		test('getWorkspaceBackups() should return [] when folderWorkspaces in workspaces.json is absent', () => {
311 312
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
313
			assert.deepEqual(service.getWorkspaceBackups(), []);
314 315
		});

316
		test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array', () => {
317
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{}}');
318
			service.loadSync();
319
			assert.deepEqual(service.getWorkspaceBackups(), []);
320
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": ["bar"]}}');
321
			service.loadSync();
322
			assert.deepEqual(service.getWorkspaceBackups(), []);
323
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": []}}');
324
			service.loadSync();
325
			assert.deepEqual(service.getWorkspaceBackups(), []);
326
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":{"foo": "bar"}}');
327
			service.loadSync();
328
			assert.deepEqual(service.getWorkspaceBackups(), []);
329
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":"foo"}');
330
			service.loadSync();
331
			assert.deepEqual(service.getWorkspaceBackups(), []);
332
			fs.writeFileSync(backupWorkspacesPath, '{"rootWorkspaces":1}');
333
			service.loadSync();
334
			assert.deepEqual(service.getWorkspaceBackups(), []);
335
		});
D
Daniel Imms 已提交
336

337 338 339 340
		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()]);
341 342
			configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
			service.loadSync();
343
			assert.deepEqual(service.getWorkspaceBackups(), []);
344 345
		});

346
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
347
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
348 349
		});

350
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => {
351 352
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
353
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
354 355
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
356
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
357 358
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
359
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
360 361
		});

362
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => {
363 364
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
365
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
366 367
		});

C
Christof Marti 已提交
368
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', function () {
C
Christof Marti 已提交
369
			this.timeout(5000);
370 371
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{}}');
			service.loadSync();
372
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
373 374
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": ["bar"]}}');
			service.loadSync();
375
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
376 377
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": []}}');
			service.loadSync();
378
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
379 380
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": "bar"}}');
			service.loadSync();
381
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
382 383
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":"foo"}');
			service.loadSync();
384
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
385 386
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":1}');
			service.loadSync();
387
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
388
		});
D
Daniel Imms 已提交
389
	});
390

391
	suite('dedupeFolderWorkspaces', () => {
392
		test('should ignore duplicates on Windows and Mac (folder workspace)', () => {
393 394 395 396 397
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}

D
Daniel Imms 已提交
398
			const backups: IBackupWorkspacesFormat = {
399
				rootWorkspaces: [],
400
				folderWorkspaces: platform.isWindows ? ['c:\\FOO', 'C:\\FOO', 'c:\\foo'] : ['/FOO', '/foo'],
D
Daniel Imms 已提交
401 402 403
				emptyWorkspaces: []
			};

404
			service.dedupeBackups(backups);
405

406
			assert.equal(backups.folderWorkspaces.length, 1);
407
			if (platform.isWindows) {
408
				assert.deepEqual(backups.folderWorkspaces, ['c:\\FOO'], 'should return the first duplicated entry');
D
Daniel Imms 已提交
409
			} else {
410
				assert.deepEqual(backups.folderWorkspaces, ['/FOO'], 'should return the first duplicated entry');
411 412
			}
		});
413 414 415 416 417 418 419 420

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

			const backups: IBackupWorkspacesFormat = {
421
				rootWorkspaces: platform.isWindows ? [toWorkspace('c:\\FOO'), toWorkspace('C:\\FOO'), toWorkspace('c:\\foo')] : [toWorkspace('/FOO'), toWorkspace('/foo')],
422 423 424 425 426 427 428 429
				folderWorkspaces: [],
				emptyWorkspaces: []
			};

			service.dedupeBackups(backups);

			assert.equal(backups.rootWorkspaces.length, 1);
			if (platform.isWindows) {
430
				assert.deepEqual(backups.rootWorkspaces.map(r => r.configPath), ['c:\\FOO'], 'should return the first duplicated entry');
431
			} else {
432
				assert.deepEqual(backups.rootWorkspaces.map(r => r.configPath), ['/FOO'], 'should return the first duplicated entry');
433 434
			}
		});
D
Daniel Imms 已提交
435 436
	});

437
	suite('registerWindowForBackups', () => {
438
		test('should persist paths to workspaces.json (folder workspace)', done => {
B
Benjamin Pasero 已提交
439 440
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(barFile.fsPath);
441
			assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath, barFile.fsPath]);
442 443 444
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath, barFile.fsPath]);
D
Daniel Imms 已提交
445
				done();
446 447
			});
		});
D
Daniel Imms 已提交
448

449
		test('should persist paths to workspaces.json (root workspace)', done => {
450 451 452 453 454 455 456 457 458
			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);

459 460
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
461 462 463 464 465

				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);

466 467 468 469 470
				done();
			});
		});

		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)', done => {
B
Benjamin Pasero 已提交
471
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
472
			assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath.toUpperCase()]);
D
Daniel Imms 已提交
473 474 475 476 477 478
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath.toUpperCase()]);
				done();
			});
		});
479 480

		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)', done => {
481 482
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
			assert.deepEqual(service.getWorkspaceBackups().map(b => b.configPath), [fooFile.fsPath.toUpperCase()]);
483 484
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
485
				assert.deepEqual(json.rootWorkspaces.map(b => b.configPath), [fooFile.fsPath.toUpperCase()]);
486 487 488
				done();
			});
		});
489 490
	});

491
	suite('removeBackupPathSync', () => {
492
		test('should remove folder workspaces from workspaces.json (folder workspace)', done => {
B
Benjamin Pasero 已提交
493 494
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(barFile.fsPath);
495
			service.removeBackupPathSync(fooFile.fsPath, service.backupsData.folderWorkspaces);
496 497 498
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [barFile.fsPath]);
499
				service.removeBackupPathSync(barFile.fsPath, service.backupsData.folderWorkspaces);
500 501 502 503 504
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.folderWorkspaces, []);
					done();
				});
505 506
			});
		});
507

508
		test('should remove folder workspaces from workspaces.json (root workspace)', done => {
509 510 511 512 513
			const ws1 = toWorkspace(fooFile.fsPath);
			service.registerWorkspaceBackupSync(ws1);
			const ws2 = toWorkspace(barFile.fsPath);
			service.registerWorkspaceBackupSync(ws2);
			service.removeBackupPathSync(ws1, service.backupsData.rootWorkspaces);
514 515
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
516 517
				assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [barFile.fsPath]);
				service.removeBackupPathSync(ws2, service.backupsData.rootWorkspaces);
518 519 520 521 522 523 524 525
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.rootWorkspaces, []);
					done();
				});
			});
		});

D
Daniel Imms 已提交
526
		test('should remove empty workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
527 528
			service.registerEmptyWindowBackupSync('foo');
			service.registerEmptyWindowBackupSync('bar');
529
			service.removeBackupPathSync('foo', service.backupsData.emptyWorkspaces);
D
Daniel Imms 已提交
530 531 532
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.emptyWorkspaces, ['bar']);
533
				service.removeBackupPathSync('bar', service.backupsData.emptyWorkspaces);
D
Daniel Imms 已提交
534 535 536 537 538 539 540 541
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.emptyWorkspaces, []);
					done();
				});
			});
		});

542
		test('should fail gracefully when removing a path that doesn\'t exist', done => {
543
			const workspacesJson: IBackupWorkspacesFormat = { rootWorkspaces: [], folderWorkspaces: [fooFile.fsPath], emptyWorkspaces: [] };
544
			pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => {
545 546
				service.removeBackupPathSync(barFile.fsPath, service.backupsData.folderWorkspaces);
				service.removeBackupPathSync('test', service.backupsData.emptyWorkspaces);
547 548 549 550 551 552 553
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath]);
					done();
				});
			});
		});
554
	});
D
Daniel Imms 已提交
555

556
	suite('getWorkspaceHash', () => {
D
Daniel Imms 已提交
557
		test('should perform an md5 hash on the path', () => {
558
			assert.equal(service.getFolderHash('/foo'), '1effb2475fcfba4f9e8b8a1dbc8f3caf');
D
Daniel Imms 已提交
559 560
		});

561 562 563 564 565
		test('should ignore case on Windows and Mac', () => {
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}
D
Daniel Imms 已提交
566

567
			if (platform.isMacintosh) {
568
				assert.equal(service.getFolderHash('/foo'), service.getFolderHash('/FOO'));
569
			}
D
Daniel Imms 已提交
570

571
			if (platform.isWindows) {
572
				assert.equal(service.getFolderHash('c:\\foo'), service.getFolderHash('C:\\FOO'));
573 574
			}
		});
D
Daniel Imms 已提交
575
	});
576

577
	suite('mixed path casing', () => {
578
		test('should handle case insensitive paths properly (registerWindowForBackupsSync) (folder workspace)', done => {
B
Benjamin Pasero 已提交
579 580
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
581

582 583 584 585 586 587 588 589 590 591
			if (platform.isLinux) {
				assert.equal(service.getFolderBackupPaths().length, 2);
			} else {
				assert.equal(service.getFolderBackupPaths().length, 1);
			}

			done();
		});

		test('should handle case insensitive paths properly (registerWindowForBackupsSync) (root workspace)', done => {
592 593
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
594

595
			if (platform.isLinux) {
596
				assert.equal(service.getWorkspaceBackups().length, 2);
597
			} else {
598
				assert.equal(service.getWorkspaceBackups().length, 1);
599 600 601 602 603
			}

			done();
		});

604
		test('should handle case insensitive paths properly (removeBackupPathSync) (folder workspace)', done => {
605 606

			// same case
B
Benjamin Pasero 已提交
607
			service.registerFolderBackupSync(fooFile.fsPath);
608 609
			service.removeBackupPathSync(fooFile.fsPath, service.backupsData.folderWorkspaces);
			assert.equal(service.getFolderBackupPaths().length, 0);
610 611

			// mixed case
B
Benjamin Pasero 已提交
612
			service.registerFolderBackupSync(fooFile.fsPath);
613 614 615 616 617 618 619 620 621 622
			service.removeBackupPathSync(fooFile.fsPath.toUpperCase(), service.backupsData.folderWorkspaces);

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

			done();
		});
623
	});
624
});