backupMainService.test.ts 15.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
import { LogMainService } from "vs/platform/log/common/log";
23

D
Daniel Imms 已提交
24
class TestBackupMainService extends BackupMainService {
B
Benjamin Pasero 已提交
25

26
	constructor(backupHome: string, backupWorkspacesPath: string, configService: TestConfigurationService) {
B
Benjamin Pasero 已提交
27
		super(new EnvironmentService(parseArgs(process.argv), process.execPath), configService, new LogMainService(new EnvironmentService(parseArgs(process.argv), process.execPath)));
28 29

		this.backupHome = backupHome;
D
Daniel Imms 已提交
30
		this.workspacesJsonPath = backupWorkspacesPath;
31 32 33

		// Force a reload with the new paths
		this.loadSync();
34
	}
35

36 37
	public removeBackupPathSync(workspaceIdenfitier: string, isEmptyWorkspace: boolean): void {
		return super.removeBackupPathSync(workspaceIdenfitier, isEmptyWorkspace);
38 39 40 41 42 43
	}

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

44 45
	public dedupeFolderWorkspaces(backups: IBackupWorkspacesFormat): IBackupWorkspacesFormat {
		return super.dedupeFolderWorkspaces(backups);
D
Daniel Imms 已提交
46 47
	}

48
	public toBackupPath(workspacePath: string): string {
D
Daniel Imms 已提交
49
		return path.join(this.backupHome, super.getWorkspaceHash(workspacePath));
50
	}
51 52 53 54

	public getWorkspaceHash(workspacePath: string): string {
		return super.getWorkspaceHash(workspacePath);
	}
55 56
}

D
Daniel Imms 已提交
57
suite('BackupMainService', () => {
D
Daniel Imms 已提交
58
	const parentDir = path.join(os.tmpdir(), 'vsctests', 'service');
59 60 61 62 63 64
	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');

65
	let service: TestBackupMainService;
66
	let configService: TestConfigurationService;
67 68

	setup(done => {
69 70
		configService = new TestConfigurationService();
		service = new TestBackupMainService(backupHome, backupWorkspacesPath, configService);
71 72 73 74

		// Delete any existing backups completely and then re-create it.
		extfs.del(backupHome, os.tmpdir(), () => {
			pfs.mkdirp(backupHome).then(() => {
D
Daniel Imms 已提交
75
				done();
76 77 78 79 80 81 82 83
			});
		});
	});

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

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

86
		// 1) backup workspace path does not exist
B
Benjamin Pasero 已提交
87 88
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
89
		service.loadSync();
90
		assert.deepEqual(service.getWorkspaceBackupPaths(), []);
91 92 93 94

		// 2) backup workspace path exists with empty contents within
		fs.mkdirSync(service.toBackupPath(fooFile.fsPath));
		fs.mkdirSync(service.toBackupPath(barFile.fsPath));
B
Benjamin Pasero 已提交
95 96
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
97
		service.loadSync();
98
		assert.deepEqual(service.getWorkspaceBackupPaths(), []);
99 100 101 102 103 104 105 106
		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 已提交
107 108
		service.registerFolderBackupSync(fooFile.fsPath);
		service.registerFolderBackupSync(barFile.fsPath);
109
		service.loadSync();
110
		assert.deepEqual(service.getWorkspaceBackupPaths(), []);
111 112 113
		assert.ok(!fs.exists(service.toBackupPath(fooFile.fsPath)));
		assert.ok(!fs.exists(service.toBackupPath(barFile.fsPath)));

114 115 116 117 118 119
		// 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 已提交
120
		service.registerFolderBackupSync(fooFile.fsPath);
121
		assert.equal(service.getWorkspaceBackupPaths().length, 1);
122
		assert.equal(service.getEmptyWindowBackupPaths().length, 0);
123 124
		fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');
		service.loadSync();
125
		assert.equal(service.getWorkspaceBackupPaths().length, 0);
126
		assert.equal(service.getEmptyWindowBackupPaths().length, 1);
127

128 129 130
		done();
	});

131
	suite('loadSync', () => {
132 133
		test('getWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
134
		});
D
Daniel Imms 已提交
135

136
		test('getWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => {
137 138
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
139
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
140 141
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
142
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
143 144
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
145
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
146
		});
D
Daniel Imms 已提交
147

148
		test('getWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => {
149 150
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
151
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
152 153
		});

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

175
		test('getWorkspaceBackupPaths() should return [] when files.hotExit = "onExitAndWindowClose"', () => {
B
Benjamin Pasero 已提交
176
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
177 178 179 180 181 182
			assert.deepEqual(service.getWorkspaceBackupPaths(), [fooFile.fsPath.toUpperCase()]);
			configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
			service.loadSync();
			assert.deepEqual(service.getWorkspaceBackupPaths(), []);
		});

183
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {
184
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
185 186
		});

187
		test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => {
188 189
			fs.writeFileSync(backupWorkspacesPath, '');
			service.loadSync();
190
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
191 192
			fs.writeFileSync(backupWorkspacesPath, '{]');
			service.loadSync();
193
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
194 195
			fs.writeFileSync(backupWorkspacesPath, 'foo');
			service.loadSync();
196
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
197 198
		});

199
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => {
200 201
			fs.writeFileSync(backupWorkspacesPath, '{}');
			service.loadSync();
202
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
203 204
		});

205
		test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', () => {
206 207
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{}}');
			service.loadSync();
208
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
209 210
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": ["bar"]}}');
			service.loadSync();
211
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
212 213
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": []}}');
			service.loadSync();
214
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
215 216
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":{"foo": "bar"}}');
			service.loadSync();
217
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
218 219
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":"foo"}');
			service.loadSync();
220
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
221 222
			fs.writeFileSync(backupWorkspacesPath, '{"emptyWorkspaces":1}');
			service.loadSync();
223
			assert.deepEqual(service.getEmptyWindowBackupPaths(), []);
224
		});
D
Daniel Imms 已提交
225
	});
226

227 228
	suite('dedupeFolderWorkspaces', () => {
		test('should ignore duplicates on Windows and Mac', () => {
229 230 231 232 233
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}

D
Daniel Imms 已提交
234
			const backups: IBackupWorkspacesFormat = {
235
				folderWorkspaces: platform.isWindows ? ['c:\\FOO', 'C:\\FOO', 'c:\\foo'] : ['/FOO', '/foo'],
D
Daniel Imms 已提交
236 237 238
				emptyWorkspaces: []
			};

239
			service.dedupeFolderWorkspaces(backups);
240

241
			assert.equal(backups.folderWorkspaces.length, 1);
242
			if (platform.isWindows) {
243
				assert.deepEqual(backups.folderWorkspaces, ['c:\\FOO'], 'should return the first duplicated entry');
D
Daniel Imms 已提交
244
			} else {
245
				assert.deepEqual(backups.folderWorkspaces, ['/FOO'], 'should return the first duplicated entry');
246 247
			}
		});
D
Daniel Imms 已提交
248 249
	});

250
	suite('registerWindowForBackups', () => {
D
Daniel Imms 已提交
251
		test('should persist paths to workspaces.json', done => {
B
Benjamin Pasero 已提交
252 253
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(barFile.fsPath);
254
			assert.deepEqual(service.getWorkspaceBackupPaths(), [fooFile.fsPath, barFile.fsPath]);
255 256 257
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath, barFile.fsPath]);
D
Daniel Imms 已提交
258
				done();
259 260
			});
		});
D
Daniel Imms 已提交
261 262

		test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive', done => {
B
Benjamin Pasero 已提交
263
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
D
Daniel Imms 已提交
264 265 266 267 268 269 270
			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();
			});
		});
271 272
	});

273
	suite('removeBackupPathSync', () => {
D
Daniel Imms 已提交
274
		test('should remove folder workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
275 276
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(barFile.fsPath);
277
			service.removeBackupPathSync(fooFile.fsPath, false);
278 279 280
			pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
				const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
				assert.deepEqual(json.folderWorkspaces, [barFile.fsPath]);
281
				service.removeBackupPathSync(barFile.fsPath, false);
282 283 284 285 286
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json2 = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json2.folderWorkspaces, []);
					done();
				});
287 288
			});
		});
289

D
Daniel Imms 已提交
290
		test('should remove empty workspaces from workspaces.json', done => {
B
Benjamin Pasero 已提交
291 292
			service.registerEmptyWindowBackupSync('foo');
			service.registerEmptyWindowBackupSync('bar');
D
Daniel Imms 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305
			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();
				});
			});
		});

306
		test('should fail gracefully when removing a path that doesn\'t exist', done => {
307 308
			const workspacesJson: IBackupWorkspacesFormat = { folderWorkspaces: [fooFile.fsPath], emptyWorkspaces: [] };
			pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => {
309
				service.removeBackupPathSync(barFile.fsPath, false);
D
Daniel Imms 已提交
310
				service.removeBackupPathSync('test', true);
311 312 313 314 315 316 317
				pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => {
					const json = <IBackupWorkspacesFormat>JSON.parse(content);
					assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath]);
					done();
				});
			});
		});
318
	});
D
Daniel Imms 已提交
319

320
	suite('getWorkspaceHash', () => {
D
Daniel Imms 已提交
321 322 323 324
		test('should perform an md5 hash on the path', () => {
			assert.equal(service.getWorkspaceHash('/foo'), '1effb2475fcfba4f9e8b8a1dbc8f3caf');
		});

325 326 327 328 329
		test('should ignore case on Windows and Mac', () => {
			// Skip test on Linux
			if (platform.isLinux) {
				return;
			}
D
Daniel Imms 已提交
330

331 332 333
			if (platform.isMacintosh) {
				assert.equal(service.getWorkspaceHash('/foo'), service.getWorkspaceHash('/FOO'));
			}
D
Daniel Imms 已提交
334

335 336 337 338
			if (platform.isWindows) {
				assert.equal(service.getWorkspaceHash('c:\\foo'), service.getWorkspaceHash('C:\\FOO'));
			}
		});
D
Daniel Imms 已提交
339
	});
340

341 342
	suite('mixed path casing', () => {
		test('should handle case insensitive paths properly (registerWindowForBackupsSync)', done => {
B
Benjamin Pasero 已提交
343 344
			service.registerFolderBackupSync(fooFile.fsPath);
			service.registerFolderBackupSync(fooFile.fsPath.toUpperCase());
345 346 347 348 349 350 351 352 353 354 355 356 357

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

			done();
		});

		test('should handle case insensitive paths properly (removeBackupPathSync)', done => {

			// same case
B
Benjamin Pasero 已提交
358
			service.registerFolderBackupSync(fooFile.fsPath);
359 360 361 362
			service.removeBackupPathSync(fooFile.fsPath, false);
			assert.equal(service.getWorkspaceBackupPaths().length, 0);

			// mixed case
B
Benjamin Pasero 已提交
363
			service.registerFolderBackupSync(fooFile.fsPath);
364 365 366 367 368 369 370 371 372 373 374
			service.removeBackupPathSync(fooFile.fsPath.toUpperCase(), false);

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

			done();
		});
	});
375
});