backupFileService.test.ts 16.4 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as assert from 'assert';
D
Daniel Imms 已提交
7
import * as platform from 'vs/base/common/platform';
R
Rob Lourens 已提交
8
import * as crypto from 'crypto';
9 10
import * as os from 'os';
import * as fs from 'fs';
11
import * as path from 'vs/base/common/path';
12
import * as pfs from 'vs/base/node/pfs';
13
import { URI as Uri } from 'vs/base/common/uri';
14
import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService';
15
import { LegacyFileService } from 'vs/workbench/services/files/node/fileService';
16
import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel';
17
import { TestContextService, TestTextResourceConfigurationService, TestEnvironmentService, TestWindowService } from 'vs/workbench/test/workbenchTestServices';
18
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
19
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
20 21
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { snapshotToString } from 'vs/platform/files/common/files';
22
import { Schemas } from 'vs/base/common/network';
B
Benjamin Pasero 已提交
23
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
24 25 26
import { FileService2 } from 'vs/workbench/services/files2/common/fileService2';
import { NullLogService } from 'vs/platform/log/common/log';
import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider';
27

B
Benjamin Pasero 已提交
28
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice');
D
Daniel Imms 已提交
29 30 31 32
const backupHome = path.join(parentDir, 'Backups');
const workspacesJsonPath = path.join(backupHome, 'workspaces.json');

const workspaceResource = Uri.file(platform.isWindows ? 'c:\\workspace' : '/workspace');
33
const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource));
34 35
const fooFile = Uri.file(platform.isWindows ? 'c:\\Foo' : '/Foo');
const barFile = Uri.file(platform.isWindows ? 'c:\\Bar' : '/Bar');
36
const untitledFile = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
37 38 39
const fooBackupPath = path.join(workspaceBackupPath, 'file', hashPath(fooFile));
const barBackupPath = path.join(workspaceBackupPath, 'file', hashPath(barFile));
const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(untitledFile));
D
Daniel Imms 已提交
40

B
Benjamin Pasero 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
class TestBackupWindowService extends TestWindowService {

	private config: IWindowConfiguration;

	constructor(workspaceBackupPath: string) {
		super();

		this.config = Object.create(null);
		this.config.backupPath = workspaceBackupPath;
	}

	getConfiguration(): IWindowConfiguration {
		return this.config;
	}
}

57 58
class TestBackupFileService extends BackupFileService {
	constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) {
59 60
		const fileService = new FileService2(new NullLogService());
		fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService()));
61
		fileService.setLegacyService(new LegacyFileService(
62 63 64 65
			fileService,
			new TestContextService(new Workspace(workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))),
			TestEnvironmentService,
			new TestTextResourceConfigurationService(),
66
		));
B
Benjamin Pasero 已提交
67
		const windowService = new TestBackupWindowService(workspaceBackupPath);
68

B
Benjamin Pasero 已提交
69
		super(windowService, fileService);
70 71
	}

72 73
	public toBackupResource(resource: Uri): Uri {
		return super.toBackupResource(resource);
74 75 76
	}
}

D
Daniel Imms 已提交
77
suite('BackupFileService', () => {
B
Benjamin Pasero 已提交
78
	let service: TestBackupFileService;
79

80
	setup(() => {
D
Daniel Imms 已提交
81
		service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);
82

83
		// Delete any existing backups completely and then re-create it.
B
Benjamin Pasero 已提交
84
		return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE).then(() => {
85 86
			return pfs.mkdirp(backupHome).then(() => {
				return pfs.writeFile(workspacesJsonPath, '');
87 88
			});
		});
89 90
	});

91
	teardown(() => {
B
Benjamin Pasero 已提交
92
		return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE);
93 94
	});

R
Rob Lourens 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
	suite('hashPath', () => {
		test('should correctly hash the path for untitled scheme URIs', () => {
			const uri = Uri.from({
				scheme: 'untitled',
				path: 'Untitled-1'
			});
			const actual = hashPath(uri);
			// If these hashes change people will lose their backed up files!
			assert.equal(actual, '13264068d108c6901b3592ea654fcd57');
			assert.equal(actual, crypto.createHash('md5').update(uri.fsPath).digest('hex'));
		});

		test('should correctly hash the path for file scheme URIs', () => {
			const uri = Uri.file('/foo');
			const actual = hashPath(uri);
			// If these hashes change people will lose their backed up files!
			if (platform.isWindows) {
				assert.equal(actual, 'dec1a583f52468a020bd120c3f01d812');
			} else {
				assert.equal(actual, '1effb2475fcfba4f9e8b8a1dbc8f3caf');
			}
			assert.equal(actual, crypto.createHash('md5').update(uri.fsPath).digest('hex'));
		});
	});

D
Daniel Imms 已提交
120 121 122 123
	suite('getBackupResource', () => {
		test('should get the correct backup path for text files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePathHash>
			const backupResource = fooFile;
124 125
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
D
Daniel Imms 已提交
126
			const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath;
127
			assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath);
D
Daniel Imms 已提交
128
		});
129

D
Daniel Imms 已提交
130 131
		test('should get the correct backup path for untitled files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePath>
132
			const backupResource = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
133 134
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
D
Daniel Imms 已提交
135
			const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath;
136
			assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath);
D
Daniel Imms 已提交
137
		});
138 139
	});

140
	suite('loadBackupResource', () => {
141 142
		test('should return whether a backup resource exists', () => {
			return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => {
D
Daniel Imms 已提交
143 144
				fs.writeFileSync(fooBackupPath, 'foo');
				service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);
145
				return service.loadBackupResource(fooFile).then(resource => {
146
					assert.ok(resource);
147
					assert.equal(path.basename(resource!.fsPath), path.basename(fooBackupPath));
148 149 150 151 152 153
					return service.hasBackups().then(hasBackups => {
						assert.ok(hasBackups);
					});
				});
			});
		});
D
Daniel Imms 已提交
154
	});
155

D
Daniel Imms 已提交
156
	suite('backupResource', () => {
157 158
		test('text file', function () {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
159 160 161
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
				assert.equal(fs.existsSync(fooBackupPath), true);
				assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
162 163 164
			});
		});

165 166
		test('untitled file', function () {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
167 168 169
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
				assert.equal(fs.existsSync(untitledBackupPath), true);
				assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`);
170 171
			});
		});
172

173
		test('text file (ITextSnapshot)', function () {
174 175
			const model = TextModel.createFromString('test');

176
			return service.backupResource(fooFile, model.createSnapshot()).then(() => {
177 178 179 180 181 182 183
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
				assert.equal(fs.existsSync(fooBackupPath), true);
				assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
				model.dispose();
			});
		});

184
		test('untitled file (ITextSnapshot)', function () {
185 186
			const model = TextModel.createFromString('test');

187
			return service.backupResource(untitledFile, model.createSnapshot()).then(() => {
188 189 190 191 192 193 194
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
				assert.equal(fs.existsSync(untitledBackupPath), true);
				assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`);
				model.dispose();
			});
		});

195
		test('text file (large file, ITextSnapshot)', function () {
B
Benjamin Pasero 已提交
196
			const largeString = (new Array(10 * 1024)).join('Large String\n');
197 198
			const model = TextModel.createFromString(largeString);

199
			return service.backupResource(fooFile, model.createSnapshot()).then(() => {
200 201 202 203 204 205 206
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
				assert.equal(fs.existsSync(fooBackupPath), true);
				assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\n${largeString}`);
				model.dispose();
			});
		});

207
		test('untitled file (large file, ITextSnapshot)', function () {
B
Benjamin Pasero 已提交
208
			const largeString = (new Array(10 * 1024)).join('Large String\n');
209 210
			const model = TextModel.createFromString(largeString);

211
			return service.backupResource(untitledFile, model.createSnapshot()).then(() => {
212 213 214 215 216 217
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
				assert.equal(fs.existsSync(untitledBackupPath), true);
				assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\n${largeString}`);
				model.dispose();
			});
		});
218 219
	});

D
Daniel Imms 已提交
220
	suite('discardResourceBackup', () => {
221 222
		test('text file', function () {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
223
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
224
				return service.discardResourceBackup(fooFile).then(() => {
225
					assert.equal(fs.existsSync(fooBackupPath), false);
D
Daniel Imms 已提交
226
					assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0);
227 228 229 230
				});
			});
		});

231 232
		test('untitled file', function () {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
233
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
234
				return service.discardResourceBackup(untitledFile).then(() => {
D
Daniel Imms 已提交
235 236 237
					assert.equal(fs.existsSync(untitledBackupPath), false);
					assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0);
				});
238 239 240
			});
		});
	});
B
Benjamin Pasero 已提交
241

D
Daniel Imms 已提交
242
	suite('discardAllWorkspaceBackups', () => {
243 244
		test('text file', function () {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
245
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
246
				return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
247
					assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2);
248
					return service.discardAllWorkspaceBackups().then(() => {
D
Daniel Imms 已提交
249 250 251
						assert.equal(fs.existsSync(fooBackupPath), false);
						assert.equal(fs.existsSync(barBackupPath), false);
						assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false);
D
Daniel Imms 已提交
252 253 254 255 256
					});
				});
			});
		});

257 258
		test('untitled file', function () {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
259
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
260
				return service.discardAllWorkspaceBackups().then(() => {
D
Daniel Imms 已提交
261 262 263
					assert.equal(fs.existsSync(untitledBackupPath), false);
					assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false);
				});
264 265
			});
		});
266

267 268 269
		test('should disable further backups', function () {
			return service.discardAllWorkspaceBackups().then(() => {
				return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
270 271 272 273
					assert.equal(fs.existsSync(workspaceBackupPath), false);
				});
			});
		});
274 275
	});

D
Daniel Imms 已提交
276
	suite('getWorkspaceFileBackups', () => {
277 278 279
		test('("file") - text file', () => {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
				return service.getWorkspaceFileBackups().then(textFiles => {
D
Daniel Imms 已提交
280
					assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]);
281 282
					return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
						return service.getWorkspaceFileBackups().then(textFiles => {
D
Daniel Imms 已提交
283 284 285 286 287 288 289
							assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]);
						});
					});
				});
			});
		});

290 291 292
		test('("file") - untitled file', () => {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
				return service.getWorkspaceFileBackups().then(textFiles => {
D
Daniel Imms 已提交
293 294 295 296 297
					assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]);
				});
			});
		});

298 299 300
		test('("untitled") - untitled file', () => {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
				return service.getWorkspaceFileBackups().then(textFiles => {
D
Daniel Imms 已提交
301 302
					assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']);
				});
303 304 305 306
			});
		});
	});

307 308 309
	test('resolveBackupContent', () => {
		test('should restore the original contents (untitled file)', () => {
			const contents = 'test\nand more stuff';
310
			service.backupResource(untitledFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
311
				service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => {
M
Matt Bierner 已提交
312
					assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
313 314 315 316 317 318 319 320 321 322 323 324
				});
			});
		});

		test('should restore the original contents (text file)', () => {
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'consectetur ',
				'adipiscing ßß elit',
			].join('');

325
			service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
326
				service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => {
M
Matt Bierner 已提交
327
					assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
328 329
				});
			});
D
Daniel Imms 已提交
330
		});
D
Daniel Imms 已提交
331
	});
D
Daniel Imms 已提交
332
});
D
Daniel Imms 已提交
333

D
Daniel Imms 已提交
334 335
suite('BackupFilesModel', () => {
	test('simple', () => {
B
Benjamin Pasero 已提交
336
		const model = new BackupFilesModel();
B
Benjamin Pasero 已提交
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381

		const resource1 = Uri.file('test.html');

		assert.equal(model.has(resource1), false);

		model.add(resource1);

		assert.equal(model.has(resource1), true);
		assert.equal(model.has(resource1, 0), true);
		assert.equal(model.has(resource1, 1), false);

		model.remove(resource1);

		assert.equal(model.has(resource1), false);

		model.add(resource1);

		assert.equal(model.has(resource1), true);
		assert.equal(model.has(resource1, 0), true);
		assert.equal(model.has(resource1, 1), false);

		model.clear();

		assert.equal(model.has(resource1), false);

		model.add(resource1, 1);

		assert.equal(model.has(resource1), true);
		assert.equal(model.has(resource1, 0), false);
		assert.equal(model.has(resource1, 1), true);

		const resource2 = Uri.file('test1.html');
		const resource3 = Uri.file('test2.html');
		const resource4 = Uri.file('test3.html');

		model.add(resource2);
		model.add(resource3);
		model.add(resource4);

		assert.equal(model.has(resource1), true);
		assert.equal(model.has(resource2), true);
		assert.equal(model.has(resource3), true);
		assert.equal(model.has(resource4), true);
	});

382 383
	test('resolve', () => {
		return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => {
B
Benjamin Pasero 已提交
384 385
			fs.writeFileSync(fooBackupPath, 'foo');

B
Benjamin Pasero 已提交
386
			const model = new BackupFilesModel();
B
Benjamin Pasero 已提交
387

388
			return model.resolve(workspaceBackupPath).then(model => {
B
Benjamin Pasero 已提交
389 390 391 392
				assert.equal(model.has(Uri.file(fooBackupPath)), true);
			});
		});
	});
393

D
Daniel Imms 已提交
394
	test('get', () => {
395 396
		const model = new BackupFilesModel();

397
		assert.deepEqual(model.get(), []);
398

D
Daniel Imms 已提交
399 400 401
		const file1 = Uri.file('/root/file/foo.html');
		const file2 = Uri.file('/root/file/bar.html');
		const untitled = Uri.file('/root/untitled/bar.html');
402

D
Daniel Imms 已提交
403 404 405 406
		model.add(file1);
		model.add(file2);
		model.add(untitled);

407
		assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]);
408
	});
409
});