backupMainService.test.ts 25.8 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';
J
Joao Moreno 已提交
22
import { LegacyLogMainService } from 'vs/platform/log/common/log';
B
Benjamin Pasero 已提交
23 24
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { createHash } from 'crypto';
B
Benjamin Pasero 已提交
25
import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices';
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) {
37
			super(environmentService, configService, new LegacyLogMainService(environmentService));
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(done => {
89 90
		configService = new TestConfigurationService();
		service = new TestBackupMainService(backupHome, backupWorkspacesPath, configService);
91 92 93 94

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

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

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

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

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
B
Benjamin Pasero 已提交
115 116
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
117
		service.loadSync();
118
		assert.deepEqual(service.getFolderBackupPaths(), []);
119 120 121 122 123 124 125 126
		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 已提交
127 128
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
129
		service.loadSync();
130
		assert.deepEqual(service.getFolderBackupPaths(), []);
131 132 133
		assert.ok(!fs.exists(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.exists(service.toBackupPath(barFile.fsPath)));

134 135 136 137 138 139
		// 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 已提交
140
		service.registerFolderBackupSync(fooFile.fsPath);
141 142 143 144 145 146 147 148 149 150 151 152 153
		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
154 155
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
156
		service.loadSync();
157
		assert.deepEqual(service.getWorkspaceBackups(), []);
158 159 160 161

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
162 163
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
164
		service.loadSync();
165
		assert.deepEqual(service.getWorkspaceBackups(), []);
166 167 168 169 170 171 172 173
		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'));
174 175
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath));
176
		service.loadSync();
177
		assert.deepEqual(service.getWorkspaceBackups(), []);
178 179 180 181 182 183 184 185 186
		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);
187 188
		service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
		assert.equal(service.getWorkspaceBackups().length, 1);
189
		assert.equal(service.getEmptyWindowBackupPaths().length, 0);
190 191
		fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
		service.loadSync();
192
		assert.equal(service.getWorkspaceBackups().length, 0);
193
		assert.equal(service.getEmptyWindowBackupPaths().length, 1);
194

195 196 197
		done();
	});

198 199 200 201 202 203 204 205 206 207 208 209
	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));

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

237 238 239
		done();
	});

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

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

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

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

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

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

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

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

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

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

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

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

402
			service.dedupeBackups(backups);
403

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

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

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

			service.dedupeBackups(backups);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

580 581 582 583 584 585 586 587 588 589
			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 => {
590 591
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath));
			service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath.toUpperCase()));
592

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

			done();
		});

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

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

			// mixed case
B
Benjamin Pasero 已提交
610
			service.registerFolderBackupSync(fooFile.fsPath);
611 612 613 614 615 616 617 618 619 620
			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();
		});
621
	});
622
});