backupMainService.test.ts 13.5 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 245 246
			});
		});
	});

247
	suite('removeBackupPathSync', () => {
D
Daniel Imms 已提交
248
		test('should remove folder workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
249 250
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(2, false, null, barFile.fsPath);
251
			service.removeBackupPathSync(fooFile.fsPath, false);
252 253 254
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [barFile.fsPath]);
255
				service.removeBackupPathSync(barFile.fsPath, false);
256 257 258 259 260
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.folderWorkspaces, []);
					done();
				});
261 262
			});
		});
263

D
Daniel Imms 已提交
264
		test('should remove empty workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
265 266
			service.registerWindowForBackupsSync(1, true, 'foo');
			service.registerWindowForBackupsSync(2, true, 'bar');
D
Daniel Imms 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279
			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();
				});
			});
		});

280
		test('should fail gracefully when removing a path that doesn\'t exist', done => {
281 282
			const workspacesJson: IBackupWorkspacesFormat = { folderWorkspaces: [fooFile.fsPath], emptyWorkspaces: [] };
			pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => {
283
				service.removeBackupPathSync(barFile.fsPath, false);
D
Daniel Imms 已提交
284
				service.removeBackupPathSync('test', true);
285 286 287 288 289 290 291
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath]);
					done();
				});
			});
		});
292
	});
D
Daniel Imms 已提交
293

294
	suite('getWorkspaceHash', () => {
D
Daniel Imms 已提交
295 296 297 298
		test('should perform an md5 hash on the path', () => {
			assert.equal(service.getWorkspaceHash('/foo'), '1effb2475fcfba4f9e8b8a1dbc8f3caf');
		});

299 300 301 302 303
		test('should ignore case on Windows and Mac', () => {
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}
D
Daniel Imms 已提交
304

305 306 307
			if (platform.isMacintosh) {
				assert.equal(service.getWorkspaceHash('/foo'), service.getWorkspaceHash('/FOO'));
			}
D
Daniel Imms 已提交
308

309 310 311 312
			if (platform.isWindows) {
				assert.equal(service.getWorkspaceHash('c:\\foo'), service.getWorkspaceHash('C:\\FOO'));
			}
		});
D
Daniel Imms 已提交
313
	});
314 315 316

	suite('getBackupPath', () => {
		test('should return the window\'s correct path', done => {
B
Benjamin Pasero 已提交
317 318
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(2, true, 'test');
319 320 321 322 323 324 325 326 327 328
			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 已提交
329 330
			service.registerWindowForBackupsSync(1, false, null, fooFile.fsPath);
			service.registerWindowForBackupsSync(1, false, null, barFile.fsPath);
331 332 333 334 335 336 337 338 339 340
			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));
		});
	});
341
});