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';
B
Benjamin Pasero 已提交
15
import { FileService } from 'vs/workbench/services/files/node/fileService';
16
import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel';
B
Benjamin Pasero 已提交
17
import { TestContextService, TestTextResourceConfigurationService, TestLifecycleService, TestEnvironmentService, TestStorageService, TestWindowService } from 'vs/workbench/test/workbenchTestServices';
18
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
19
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
20
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
B
Benjamin Pasero 已提交
21
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
22 23
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { snapshotToString } from 'vs/platform/files/common/files';
24
import { Schemas } from 'vs/base/common/network';
B
Benjamin Pasero 已提交
25
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
26

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

const workspaceResource = Uri.file(platform.isWindows ? 'c:\\workspace' : '/workspace');
32
const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource));
33 34
const fooFile = Uri.file(platform.isWindows ? 'c:\\Foo' : '/Foo');
const barFile = Uri.file(platform.isWindows ? 'c:\\Bar' : '/Bar');
35
const untitledFile = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
36 37 38
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 已提交
39

B
Benjamin Pasero 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
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;
	}
}

56 57
class TestBackupFileService extends BackupFileService {
	constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) {
I
isidor 已提交
58
		const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true });
B
Benjamin Pasero 已提交
59
		const windowService = new TestBackupWindowService(workspaceBackupPath);
60

B
Benjamin Pasero 已提交
61
		super(windowService, fileService);
62 63
	}

64 65
	public toBackupResource(resource: Uri): Uri {
		return super.toBackupResource(resource);
66 67 68
	}
}

D
Daniel Imms 已提交
69
suite('BackupFileService', () => {
B
Benjamin Pasero 已提交
70
	let service: TestBackupFileService;
71

72
	setup(() => {
D
Daniel Imms 已提交
73
		service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);
74

75
		// Delete any existing backups completely and then re-create it.
B
Benjamin Pasero 已提交
76
		return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE).then(() => {
77 78
			return pfs.mkdirp(backupHome).then(() => {
				return pfs.writeFile(workspacesJsonPath, '');
79 80
			});
		});
81 82
	});

83
	teardown(() => {
B
Benjamin Pasero 已提交
84
		return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE);
85 86
	});

R
Rob Lourens 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
	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 已提交
112 113 114 115
	suite('getBackupResource', () => {
		test('should get the correct backup path for text files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePathHash>
			const backupResource = fooFile;
116 117
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
D
Daniel Imms 已提交
118
			const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath;
119
			assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath);
D
Daniel Imms 已提交
120
		});
121

D
Daniel Imms 已提交
122 123
		test('should get the correct backup path for untitled files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePath>
124
			const backupResource = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
125 126
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
D
Daniel Imms 已提交
127
			const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath;
128
			assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath);
D
Daniel Imms 已提交
129
		});
130 131
	});

132
	suite('loadBackupResource', () => {
133 134
		test('should return whether a backup resource exists', () => {
			return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => {
D
Daniel Imms 已提交
135 136
				fs.writeFileSync(fooBackupPath, 'foo');
				service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);
137
				return service.loadBackupResource(fooFile).then(resource => {
138
					assert.ok(resource);
139
					assert.equal(path.basename(resource!.fsPath), path.basename(fooBackupPath));
140 141 142 143 144 145
					return service.hasBackups().then(hasBackups => {
						assert.ok(hasBackups);
					});
				});
			});
		});
D
Daniel Imms 已提交
146
	});
147

D
Daniel Imms 已提交
148
	suite('backupResource', () => {
149 150
		test('text file', function () {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
151 152 153
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
				assert.equal(fs.existsSync(fooBackupPath), true);
				assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
154 155 156
			});
		});

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

165
		test('text file (ITextSnapshot)', function () {
166 167
			const model = TextModel.createFromString('test');

168
			return service.backupResource(fooFile, model.createSnapshot()).then(() => {
169 170 171 172 173 174 175
				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();
			});
		});

176
		test('untitled file (ITextSnapshot)', function () {
177 178
			const model = TextModel.createFromString('test');

179
			return service.backupResource(untitledFile, model.createSnapshot()).then(() => {
180 181 182 183 184 185 186
				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();
			});
		});

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

191
			return service.backupResource(fooFile, model.createSnapshot()).then(() => {
192 193 194 195 196 197 198
				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();
			});
		});

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

203
			return service.backupResource(untitledFile, model.createSnapshot()).then(() => {
204 205 206 207 208 209
				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();
			});
		});
210 211
	});

D
Daniel Imms 已提交
212
	suite('discardResourceBackup', () => {
213 214
		test('text file', function () {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
215
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
216
				return service.discardResourceBackup(fooFile).then(() => {
217
					assert.equal(fs.existsSync(fooBackupPath), false);
D
Daniel Imms 已提交
218
					assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0);
219 220 221 222
				});
			});
		});

223 224
		test('untitled file', function () {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
225
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
226
				return service.discardResourceBackup(untitledFile).then(() => {
D
Daniel Imms 已提交
227 228 229
					assert.equal(fs.existsSync(untitledBackupPath), false);
					assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0);
				});
230 231 232
			});
		});
	});
B
Benjamin Pasero 已提交
233

D
Daniel Imms 已提交
234
	suite('discardAllWorkspaceBackups', () => {
235 236
		test('text file', function () {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
237
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
238
				return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
239
					assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2);
240
					return service.discardAllWorkspaceBackups().then(() => {
D
Daniel Imms 已提交
241 242 243
						assert.equal(fs.existsSync(fooBackupPath), false);
						assert.equal(fs.existsSync(barBackupPath), false);
						assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false);
D
Daniel Imms 已提交
244 245 246 247 248
					});
				});
			});
		});

249 250
		test('untitled file', function () {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
D
Daniel Imms 已提交
251
				assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
252
				return service.discardAllWorkspaceBackups().then(() => {
D
Daniel Imms 已提交
253 254 255
					assert.equal(fs.existsSync(untitledBackupPath), false);
					assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false);
				});
256 257
			});
		});
258

259 260 261
		test('should disable further backups', function () {
			return service.discardAllWorkspaceBackups().then(() => {
				return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
262 263 264 265
					assert.equal(fs.existsSync(workspaceBackupPath), false);
				});
			});
		});
266 267
	});

D
Daniel Imms 已提交
268
	suite('getWorkspaceFileBackups', () => {
269 270 271
		test('("file") - text file', () => {
			return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
				return service.getWorkspaceFileBackups().then(textFiles => {
D
Daniel Imms 已提交
272
					assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]);
273 274
					return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
						return service.getWorkspaceFileBackups().then(textFiles => {
D
Daniel Imms 已提交
275 276 277 278 279 280 281
							assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]);
						});
					});
				});
			});
		});

282 283 284
		test('("file") - untitled file', () => {
			return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
				return service.getWorkspaceFileBackups().then(textFiles => {
D
Daniel Imms 已提交
285 286 287 288 289
					assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]);
				});
			});
		});

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

299 300 301
	test('resolveBackupContent', () => {
		test('should restore the original contents (untitled file)', () => {
			const contents = 'test\nand more stuff';
302
			service.backupResource(untitledFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
303
				service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => {
M
Matt Bierner 已提交
304
					assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
305 306 307 308 309 310 311 312 313 314 315 316
				});
			});
		});

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

317
			service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => {
318
				service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => {
M
Matt Bierner 已提交
319
					assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
320 321
				});
			});
D
Daniel Imms 已提交
322
		});
D
Daniel Imms 已提交
323
	});
D
Daniel Imms 已提交
324
});
D
Daniel Imms 已提交
325

D
Daniel Imms 已提交
326 327
suite('BackupFilesModel', () => {
	test('simple', () => {
B
Benjamin Pasero 已提交
328
		const model = new BackupFilesModel();
B
Benjamin Pasero 已提交
329 330 331 332 333 334 335 336 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

		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);
	});

374 375
	test('resolve', () => {
		return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => {
B
Benjamin Pasero 已提交
376 377
			fs.writeFileSync(fooBackupPath, 'foo');

B
Benjamin Pasero 已提交
378
			const model = new BackupFilesModel();
B
Benjamin Pasero 已提交
379

380
			return model.resolve(workspaceBackupPath).then(model => {
B
Benjamin Pasero 已提交
381 382 383 384
				assert.equal(model.has(Uri.file(fooBackupPath)), true);
			});
		});
	});
385

D
Daniel Imms 已提交
386
	test('get', () => {
387 388
		const model = new BackupFilesModel();

389
		assert.deepEqual(model.get(), []);
390

D
Daniel Imms 已提交
391 392 393
		const file1 = Uri.file('/root/file/foo.html');
		const file2 = Uri.file('/root/file/bar.html');
		const untitled = Uri.file('/root/untitled/bar.html');
394

D
Daniel Imms 已提交
395 396 397 398
		model.add(file1);
		model.add(file2);
		model.add(untitled);

399
		assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]);
400
	});
401
});