backupMainService.test.ts 14.1 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 16
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';
import { TestEnvironmentService } from 'vs/test/utils/servicesTestUtils';
D
Daniel Imms 已提交
17
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
18
import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup';
19

D
Daniel Imms 已提交
20
class TestBackupMainService extends BackupMainService {
21
	constructor(backupHome: string, backupWorkspacesPath: string) {
22
		super(TestEnvironmentService);
23 24

		this.backupHome = backupHome;
D
Daniel Imms 已提交
25
		this.workspacesJsonPath = backupWorkspacesPath;
26 27 28

		// Force a reload with the new paths
		this.loadSync();
29
	}
30

31 32
	public removeBackupPathSync(workspaceIdenfitier: string, isEmptyWorkspace: boolean): void {
		return super.removeBackupPathSync(workspaceIdenfitier, isEmptyWorkspace);
33 34 35 36 37 38
	}

	public loadSync(): void {
		super.loadSync();
	}

39 40
	public dedupeFolderWorkspaces(backups: IBackupWorkspacesFormat): void {
		super.dedupeFolderWorkspaces(backups);
D
Daniel Imms 已提交
41 42
	}

43
	public toBackupPath(workspacePath: string): string {
D
Daniel Imms 已提交
44
		return path.join(this.backupHome, super.getWorkspaceHash(workspacePath));
45
	}
46 47 48 49

	public getWorkspaceHash(workspacePath: string): string {
		return super.getWorkspaceHash(workspacePath);
	}
50 51
}

D
Daniel Imms 已提交
52
suite('BackupMainService', () => {
D
Daniel Imms 已提交
53
	const parentDir = path.join(os.tmpdir(), 'vsctests', 'service');
54 55 56 57 58 59
	const backupHome = path.join(parentDir, 'Backups');
	const backupWorkspacesPath = path.join(backupHome, 'workspaces.json');

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

60
	let service: TestBackupMainService;
61 62

	setup(done => {
D
Daniel Imms 已提交
63
		service = new TestBackupMainService(backupHome, backupWorkspacesPath);
64 65 66 67

		// Delete any existing backups completely and then re-create it.
		extfs.del(backupHome, os.tmpdir(), () => {
			pfs.mkdirp(backupHome).then(() => {
D
Daniel Imms 已提交
68
				done();
69 70 71 72 73 74 75 76
			});
		});
	});

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

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

79
		// 1) backup workspace path does not exist
B
Benjamin Pasero 已提交
80 81
		service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
		service.registerWindowForBackupsSync(2, false, null, barFile.fsPath);
82
		service.loadSync();
83
		assert.deepEqual(service.getWorkspaceBackupPaths(), []);
84 85 86 87

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
B
Benjamin Pasero 已提交
88 89
		service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
		service.registerWindowForBackupsSync(2, false, null, barFile.fsPath);
90
		service.loadSync();
91
		assert.deepEqual(service.getWorkspaceBackupPaths(), []);
92 93 94 95 96 97 98 99
		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 已提交
100 101
		service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
		service.registerWindowForBackupsSync(2, false, null, barFile.fsPath);
102
		service.loadSync();
103
		assert.deepEqual(service.getWorkspaceBackupPaths(), []);
104 105 106
		assert.ok(!fs.exists(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.exists(service.toBackupPath(barFile.fsPath)));

107 108 109 110 111 112
		// 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 已提交
113
		service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
114 115
		assert.equal(service.getWorkspaceBackupPaths().length, 1);
		assert.equal(service.getEmptyWorkspaceBackupPaths().length, 0);
116 117
		fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
		service.loadSync();
118 119
		assert.equal(service.getWorkspaceBackupPaths().length, 0);
		assert.equal(service.getEmptyWorkspaceBackupPaths().length, 1);
120

121 122 123
		done();
	});

124
	suite('loadSync', () => {
125 126
		test('getWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
127
		});
D
Daniel Imms 已提交
128

129
		test('getWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => {
130 131
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
132
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
133 134
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
135
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
136 137
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
138
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
139
		});
D
Daniel Imms 已提交
140

141
		test('getWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => {
142 143
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
144
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
145 146
		});

147
		test('getWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', () => {
148 149
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{}}');
			service.loadSync();
150
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
151 152
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": ["bar"]}}');
			service.loadSync();
153
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
154 155
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": []}}');
			service.loadSync();
156
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
157 158
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": "bar"}}');
			service.loadSync();
159
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
160 161
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":"foo"}');
			service.loadSync();
162
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
163 164
			fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":1}');
			service.loadSync();
165
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
166
		});
D
Daniel Imms 已提交
167

168 169
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
170 171
		});

172
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => {
173 174
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
175
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
176 177
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
178
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
179 180
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
181
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
182 183
		});

184
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => {
185 186
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
187
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
188 189
		});

190
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', () => {
191 192
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{}}');
			service.loadSync();
193
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
194 195
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": ["bar"]}}');
			service.loadSync();
196
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
197 198
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": []}}');
			service.loadSync();
199
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
200 201
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": "bar"}}');
			service.loadSync();
202
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
203 204
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":"foo"}');
			service.loadSync();
205
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
206 207
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":1}');
			service.loadSync();
208
			assert.deepEqual(service.getEmptyWorkspaceBackupPaths(), []);
209
		});
D
Daniel Imms 已提交
210
	});
211

212 213
	suite('dedupeFolderWorkspaces', () => {
		test('should ignore duplicates on Windows and Mac', () => {
214 215 216 217 218
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}

D
Daniel Imms 已提交
219
			const backups: IBackupWorkspacesFormat = {
220
				folderWorkspaces: platform.isWindows ? ['c:\\FOO', 'C:\\FOO', 'c:\\foo'] : ['/FOO', '/foo'],
D
Daniel Imms 已提交
221 222 223
				emptyWorkspaces: []
			};

224
			service.dedupeFolderWorkspaces(backups);
225

226
			assert.equal(backups.folderWorkspaces.length, 1);
227
			if (platform.isWindows) {
228
				assert.deepEqual(backups.folderWorkspaces, ['c:\\FOO'], 'should return the first duplicated entry');
D
Daniel Imms 已提交
229
			} else {
230
				assert.deepEqual(backups.folderWorkspaces, ['/FOO'], 'should return the first duplicated entry');
231 232
			}
		});
D
Daniel Imms 已提交
233 234
	});

235
	suite('registerWindowForBackups', () => {
D
Daniel Imms 已提交
236
		test('should persist paths to workspaces.json', done => {
B
Benjamin Pasero 已提交
237 238
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(2, false, null, barFile.fsPath);
239
			assert.deepEqual(service.getWorkspaceBackupPaths(), [fooFile.fsPath, barFile.fsPath]);
240 241 242
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath, barFile.fsPath]);
D
Daniel Imms 已提交
243
				done();
244 245
			});
		});
D
Daniel Imms 已提交
246 247 248 249 250 251 252 253 254 255

		test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive', done => {
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath.toUpperCase());
			assert.deepEqual(service.getWorkspaceBackupPaths(), [fooFile.fsPath.toUpperCase()]);
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath.toUpperCase()]);
				done();
			});
		});
256 257
	});

258
	suite('removeBackupPathSync', () => {
D
Daniel Imms 已提交
259
		test('should remove folder workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
260 261
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(2, false, null, barFile.fsPath);
262
			service.removeBackupPathSync(fooFile.fsPath, false);
263 264 265
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [barFile.fsPath]);
266
				service.removeBackupPathSync(barFile.fsPath, false);
267 268 269 270 271
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.folderWorkspaces, []);
					done();
				});
272 273
			});
		});
274

D
Daniel Imms 已提交
275
		test('should remove empty workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
276 277
			service.registerWindowForBackupsSync(1, true, 'foo');
			service.registerWindowForBackupsSync(2, true, 'bar');
D
Daniel Imms 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290
			service.removeBackupPathSync('foo', true);
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.emptyWorkspaces, ['bar']);
				service.removeBackupPathSync('bar', true);
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.emptyWorkspaces, []);
					done();
				});
			});
		});

291
		test('should fail gracefully when removing a path that doesn\'t exist', done => {
292 293
			const workspacesJson: IBackupWorkspacesFormat = { folderWorkspaces: [fooFile.fsPath], emptyWorkspaces: [] };
			pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => {
294
				service.removeBackupPathSync(barFile.fsPath, false);
D
Daniel Imms 已提交
295
				service.removeBackupPathSync('test', true);
296 297 298 299 300 301 302
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath]);
					done();
				});
			});
		});
303
	});
D
Daniel Imms 已提交
304

305
	suite('getWorkspaceHash', () => {
D
Daniel Imms 已提交
306 307 308 309
		test('should perform an md5 hash on the path', () => {
			assert.equal(service.getWorkspaceHash('/foo'), '1effb2475fcfba4f9e8b8a1dbc8f3caf');
		});

310 311 312 313 314
		test('should ignore case on Windows and Mac', () => {
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}
D
Daniel Imms 已提交
315

316 317 318
			if (platform.isMacintosh) {
				assert.equal(service.getWorkspaceHash('/foo'), service.getWorkspaceHash('/FOO'));
			}
D
Daniel Imms 已提交
319

320 321 322 323
			if (platform.isWindows) {
				assert.equal(service.getWorkspaceHash('c:\\foo'), service.getWorkspaceHash('C:\\FOO'));
			}
		});
D
Daniel Imms 已提交
324
	});
325 326 327

	suite('getBackupPath', () => {
		test('should return the window\'s correct path', done => {
B
Benjamin Pasero 已提交
328 329
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(2, true, 'test');
330 331 332 333 334 335 336 337 338 339
			service.getBackupPath(1).then(window1Path => {
				assert.equal(window1Path, service.toBackupPath(fooFile.fsPath));
				service.getBackupPath(2).then(window2Path => {
					assert.equal(window2Path, path.join(backupHome, 'test'));
					done();
				});
			});
		});

		test('should override stale window paths with new paths', done => {
B
Benjamin Pasero 已提交
340 341
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(1, false, null, barFile.fsPath);
342 343 344 345 346 347 348 349 350 351
			service.getBackupPath(1).then(windowPath => {
				assert.equal(windowPath, service.toBackupPath(barFile.fsPath));
				done();
			});
		});

		test('should throw when the window is not registered', () => {
			assert.throws(() => service.getBackupPath(1));
		});
	});
352
});