backupMainService.test.ts 14.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 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();
	}

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

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

D
Daniel Imms 已提交
212
	suite('sanitizeFolderWorkspaces', () => {
213 214 215 216 217 218
		test('should merge same cased paths on Windows and Mac', () => {
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}

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

			service.sanitizeFolderWorkspaces(backups);
225 226

			if (platform.isWindows) {
D
Daniel Imms 已提交
227 228 229
				assert.deepEqual(backups.folderWorkspaces, ['c:\\foo']);
			} else {
				assert.deepEqual(backups.folderWorkspaces, ['/foo']);
230 231
			}
		});
D
Daniel Imms 已提交
232 233
	});

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

		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();
			});
		});
255 256
	});

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

D
Daniel Imms 已提交
274
		test('should remove empty workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
275 276
			service.registerWindowForBackupsSync(1, true, 'foo');
			service.registerWindowForBackupsSync(2, true, 'bar');
D
Daniel Imms 已提交
277 278 279 280 281 282 283 284 285 286 287 288 289
			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();
				});
			});
		});

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

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

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

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

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

	suite('getBackupPath', () => {
		test('should return the window\'s correct path', done => {
B
Benjamin Pasero 已提交
327 328
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(2, true, 'test');
329 330 331 332 333 334 335 336 337 338
			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 已提交
339 340
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(1, false, null, barFile.fsPath);
341 342 343 344 345 346 347 348 349 350
			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));
		});
	});
351
});