backupFileService.test.ts 23.2 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';
B
Benjamin Pasero 已提交
13
import { URI } from 'vs/base/common/uri';
14
import { BackupFilesModel } from 'vs/workbench/services/backup/common/backupFileService';
15
import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel';
16
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
17
import { DefaultEndOfLine, ITextSnapshot } from 'vs/editor/common/model';
18
import { Schemas } from 'vs/base/common/network';
19
import { FileService } from 'vs/platform/files/common/fileService';
20
import { NullLogService } from 'vs/platform/log/common/log';
21
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
22
import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
23
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
24
import { IFileService } from 'vs/platform/files/common/files';
25
import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
26 27
import { BACKUPS } from 'vs/platform/environment/common/environment';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
B
Benjamin Pasero 已提交
28
import { VSBuffer } from 'vs/base/common/buffer';
M
Martin Aeschlimann 已提交
29
import { TestWindowConfiguration } from 'vs/workbench/test/workbenchTestServices';
30

31 32 33
const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice');
const appSettingsHome = path.join(userdataDir, 'User');
const backupHome = path.join(userdataDir, 'Backups');
D
Daniel Imms 已提交
34 35
const workspacesJsonPath = path.join(backupHome, 'workspaces.json');

B
Benjamin Pasero 已提交
36
const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/workspace');
37
const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource));
B
Benjamin Pasero 已提交
38 39
const fooFile = URI.file(platform.isWindows ? 'c:\\Foo' : '/Foo');
const customFile = URI.parse('customScheme://some/path');
40
const customFileWithFragment = URI.parse('customScheme2://some/path#fragment');
B
Benjamin Pasero 已提交
41 42 43
const barFile = URI.file(platform.isWindows ? 'c:\\Bar' : '/Bar');
const fooBarFile = URI.file(platform.isWindows ? 'c:\\Foo Bar' : '/Foo Bar');
const untitledFile = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
44 45 46
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 已提交
47

48
class TestBackupEnvironmentService extends NativeWorkbenchEnvironmentService {
B
Benjamin Pasero 已提交
49

50
	constructor(backupPath: string) {
M
Martin Aeschlimann 已提交
51
		super({ ...TestWindowConfiguration, backupPath, 'user-data-dir': userdataDir }, TestWindowConfiguration.execPath, TestWindowConfiguration.windowId);
B
Benjamin Pasero 已提交
52 53 54
	}
}

55
export class NodeTestBackupFileService extends BackupFileService {
56 57 58

	readonly fileService: IFileService;

59 60 61
	private backupResourceJoiners: Function[];
	private discardBackupJoiners: Function[];

62
	constructor(workspaceBackupPath: string) {
63
		const environmentService = new TestBackupEnvironmentService(workspaceBackupPath);
64 65 66
		const fileService = new FileService(new NullLogService());
		const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
		fileService.registerProvider(Schemas.file, diskFileSystemProvider);
S
Sandeep Somavarapu 已提交
67
		fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService));
68

69
		super(environmentService, fileService);
70 71

		this.fileService = fileService;
72 73
		this.backupResourceJoiners = [];
		this.discardBackupJoiners = [];
74
		this.didDiscardAllBackups = false;
75 76
	}

77 78 79 80
	joinBackupResource(): Promise<void> {
		return new Promise(resolve => this.backupResourceJoiners.push(resolve));
	}

81
	async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: any): Promise<void> {
B
Benjamin Pasero 已提交
82
		await super.backup(resource, content, versionId, meta);
83 84 85 86 87 88 89 90 91 92

		while (this.backupResourceJoiners.length) {
			this.backupResourceJoiners.pop()!();
		}
	}

	joinDiscardBackup(): Promise<void> {
		return new Promise(resolve => this.discardBackupJoiners.push(resolve));
	}

B
Benjamin Pasero 已提交
93 94
	async discardBackup(resource: URI): Promise<void> {
		await super.discardBackup(resource);
95 96 97 98 99

		while (this.discardBackupJoiners.length) {
			this.discardBackupJoiners.pop()!();
		}
	}
100

101
	didDiscardAllBackups: boolean;
102

103 104
	discardAllBackups(): Promise<void> {
		this.didDiscardAllBackups = true;
105

106
		return super.discardAllBackups();
107
	}
108 109
}

D
Daniel Imms 已提交
110
suite('BackupFileService', () => {
111
	let service: NodeTestBackupFileService;
112

113
	setup(async () => {
114
		service = new NodeTestBackupFileService(workspaceBackupPath);
115

116
		// Delete any existing backups completely and then re-create it.
117 118 119 120
		await pfs.rimraf(backupHome, pfs.RimRafMode.MOVE);
		await pfs.mkdirp(backupHome);

		return pfs.writeFile(workspacesJsonPath, '');
121 122
	});

123
	teardown(() => {
B
Benjamin Pasero 已提交
124
		return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE);
125 126
	});

R
Rob Lourens 已提交
127 128
	suite('hashPath', () => {
		test('should correctly hash the path for untitled scheme URIs', () => {
B
Benjamin Pasero 已提交
129
			const uri = URI.from({
R
Rob Lourens 已提交
130 131 132 133 134 135 136 137 138 139
				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', () => {
B
Benjamin Pasero 已提交
140
			const uri = URI.file('/foo');
R
Rob Lourens 已提交
141 142 143 144 145 146 147 148 149 150 151
			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 已提交
152 153 154 155
	suite('getBackupResource', () => {
		test('should get the correct backup path for text files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePathHash>
			const backupResource = fooFile;
156 157
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
158 159
			const expectedPath = URI.file(path.join(appSettingsHome, BACKUPS, workspaceHash, Schemas.file, filePathHash)).with({ scheme: Schemas.userData }).toString();
			assert.equal(service.toBackupResource(backupResource).toString(), expectedPath);
D
Daniel Imms 已提交
160
		});
161

D
Daniel Imms 已提交
162 163
		test('should get the correct backup path for untitled files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePath>
B
Benjamin Pasero 已提交
164
			const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
165 166
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
167 168
			const expectedPath = URI.file(path.join(appSettingsHome, BACKUPS, workspaceHash, Schemas.untitled, filePathHash)).with({ scheme: Schemas.userData }).toString();
			assert.equal(service.toBackupResource(backupResource).toString(), expectedPath);
D
Daniel Imms 已提交
169
		});
170 171
	});

172
	suite('backup', () => {
173 174 175 176 177 178 179 180
		test('no text', async () => {
			await service.backup(fooFile);
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
			assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\n`);
			assert.ok(service.hasBackupSync(fooFile));
		});

181
		test('text file', async () => {
B
Benjamin Pasero 已提交
182
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
183 184 185
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
			assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
186 187 188 189
			assert.ok(service.hasBackupSync(fooFile));
		});

		test('text file (with version)', async () => {
B
Benjamin Pasero 已提交
190
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), 666);
191 192 193 194 195
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
			assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
			assert.ok(!service.hasBackupSync(fooFile, 555));
			assert.ok(service.hasBackupSync(fooFile, 666));
196 197
		});

198
		test('text file (with meta)', async () => {
B
Benjamin Pasero 已提交
199
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), undefined, { etag: '678', orphaned: true });
200 201
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
202
			assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`);
203
			assert.ok(service.hasBackupSync(fooFile));
204 205
		});

206
		test('untitled file', async () => {
B
Benjamin Pasero 已提交
207
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
208 209 210
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
			assert.equal(fs.existsSync(untitledBackupPath), true);
			assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`);
211
			assert.ok(service.hasBackupSync(untitledFile));
212
		});
213

214
		test('text file (ITextSnapshot)', async () => {
215 216
			const model = TextModel.createFromString('test');

B
Benjamin Pasero 已提交
217
			await service.backup(fooFile, model.createSnapshot());
218 219 220
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
			assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
221 222
			assert.ok(service.hasBackupSync(fooFile));

223
			model.dispose();
224 225
		});

226
		test('untitled file (ITextSnapshot)', async () => {
227 228
			const model = TextModel.createFromString('test');

B
Benjamin Pasero 已提交
229
			await service.backup(untitledFile, model.createSnapshot());
230 231 232
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
			assert.equal(fs.existsSync(untitledBackupPath), true);
			assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`);
233

234
			model.dispose();
235 236
		});

237
		test('text file (large file, ITextSnapshot)', async () => {
B
Benjamin Pasero 已提交
238
			const largeString = (new Array(10 * 1024)).join('Large String\n');
239 240
			const model = TextModel.createFromString(largeString);

B
Benjamin Pasero 已提交
241
			await service.backup(fooFile, model.createSnapshot());
242 243 244
			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}`);
245 246
			assert.ok(service.hasBackupSync(fooFile));

247
			model.dispose();
248 249
		});

250
		test('untitled file (large file, ITextSnapshot)', async () => {
B
Benjamin Pasero 已提交
251
			const largeString = (new Array(10 * 1024)).join('Large String\n');
252 253
			const model = TextModel.createFromString(largeString);

B
Benjamin Pasero 已提交
254
			await service.backup(untitledFile, model.createSnapshot());
255 256 257
			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}`);
258 259
			assert.ok(service.hasBackupSync(untitledFile));

260
			model.dispose();
261
		});
262 263
	});

264
	suite('discardBackup', () => {
265
		test('text file', async () => {
B
Benjamin Pasero 已提交
266
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
267
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
268 269
			assert.ok(service.hasBackupSync(fooFile));

B
Benjamin Pasero 已提交
270
			await service.discardBackup(fooFile);
271 272
			assert.equal(fs.existsSync(fooBackupPath), false);
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0);
273
			assert.ok(!service.hasBackupSync(fooFile));
274 275
		});

276
		test('untitled file', async () => {
B
Benjamin Pasero 已提交
277
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
278
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
B
Benjamin Pasero 已提交
279
			await service.discardBackup(untitledFile);
280 281
			assert.equal(fs.existsSync(untitledBackupPath), false);
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0);
282 283
		});
	});
B
Benjamin Pasero 已提交
284

285
	suite('discardAllBackups', () => {
286
		test('text file', async () => {
B
Benjamin Pasero 已提交
287
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
288
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
B
Benjamin Pasero 已提交
289
			await service.backup(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
290
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2);
291
			await service.discardAllBackups();
292 293
			assert.equal(fs.existsSync(fooBackupPath), false);
			assert.equal(fs.existsSync(barBackupPath), false);
294
			assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false);
D
Daniel Imms 已提交
295 296
		});

297
		test('untitled file', async () => {
B
Benjamin Pasero 已提交
298
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
299
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
300
			await service.discardAllBackups();
301
			assert.equal(fs.existsSync(untitledBackupPath), false);
302
			assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false);
303
		});
304 305
	});

306
	suite('getBackups', () => {
307
		test('("file") - text file', async () => {
B
Benjamin Pasero 已提交
308 309
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getBackups();
310
			assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]);
B
Benjamin Pasero 已提交
311 312
			await service.backup(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles_1 = await service.getBackups();
313
			assert.deepEqual(textFiles_1.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]);
D
Daniel Imms 已提交
314 315
		});

316
		test('("file") - untitled file', async () => {
B
Benjamin Pasero 已提交
317 318
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getBackups();
319
			assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]);
D
Daniel Imms 已提交
320 321
		});

322
		test('("untitled") - untitled file', async () => {
B
Benjamin Pasero 已提交
323 324
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getBackups();
325
			assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']);
326 327 328
		});
	});

329
	suite('resolve', () => {
330 331 332 333 334 335 336 337

		interface IBackupTestMetaData {
			mtime?: number;
			size?: number;
			etag?: string;
			orphaned?: boolean;
		}

338
		test('should restore the original contents (untitled file)', async () => {
339
			const contents = 'test\nand more stuff';
340

341 342 343 344 345 346 347 348 349 350 351 352
			await testResolveBackup(untitledFile, contents);
		});

		test('should restore the original contents (untitled file with metadata)', async () => {
			const contents = 'test\nand more stuff';

			const meta = {
				etag: 'the Etag',
				size: 666,
				mtime: Date.now(),
				orphaned: true
			};
353

354
			await testResolveBackup(untitledFile, contents, meta);
355 356
		});

357
		test('should restore the original contents (text file)', async () => {
358 359 360 361
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'consectetur ',
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
				'adipiscing ßß elit'
			].join('');

			await testResolveBackup(fooFile, contents);
		});

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

			await testResolveBackup(customFile, contents);
		});

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

			const meta = {
				etag: 'theEtag',
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};

			await testResolveBackup(fooFile, contents, meta);
		});

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

			const meta = {
				etag: 'theEtag',
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};

			await testResolveBackup(fooFile, contents, meta);

			// Change meta and test again
			meta.size = 999;
			await testResolveBackup(fooFile, contents, meta);
		});

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

427 428 429 430 431 432
			const meta = {
				etag: 'theEtag',
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};
433

B
Benjamin Pasero 已提交
434
			await service.backup(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta);
435 436 437 438 439 440 441 442

			const fileContents = fs.readFileSync(fooBackupPath).toString();
			assert.equal(fileContents.indexOf(fooFile.toString()), 0);

			const metaIndex = fileContents.indexOf('{');
			const newFileContents = fileContents.substring(0, metaIndex) + '{{' + fileContents.substr(metaIndex);
			fs.writeFileSync(fooBackupPath, newFileContents);

443 444 445 446
			const backup = await service.resolve(fooFile);
			assert.ok(backup);
			assert.equal(contents, snapshotToString(backup!.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
			assert.ok(!backup!.meta);
447 448
		});

449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
		test('should restore the original contents (text file with metadata and fragment URI)', async () => {
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'adipiscing ßß elit',
				'consectetur '
			].join('');

			const meta = {
				etag: 'theEtag',
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};

			await testResolveBackup(customFileWithFragment, contents, meta);
		});

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
		test('should restore the original contents (text file with space in name with metadata)', async () => {
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'adipiscing ßß elit',
				'consectetur '
			].join('');

			const meta = {
				etag: 'theEtag',
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};

			await testResolveBackup(fooBarFile, contents, meta);
D
Daniel Imms 已提交
483
		});
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502

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

			const meta = {
				etag: (new Array(100 * 1024)).join('Large String'),
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};

			await testResolveBackup(fooBarFile, contents, meta, null);
		});

B
Benjamin Pasero 已提交
503 504 505
		test('should throw an error when restoring invalid backup', async () => {
			const contents = 'test\nand more stuff';

B
Benjamin Pasero 已提交
506
			await service.backup(fooBarFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1);
B
Benjamin Pasero 已提交
507

508
			const backup = await service.resolve(fooBarFile);
B
Benjamin Pasero 已提交
509 510 511 512
			if (!backup) {
				throw new Error('Unexpected missing backup');
			}

513
			await service.fileService.writeFile(service.toBackupResource(fooBarFile), VSBuffer.fromString(''));
B
Benjamin Pasero 已提交
514 515 516

			let err: Error;
			try {
517
				await service.resolve<IBackupTestMetaData>(fooBarFile);
B
Benjamin Pasero 已提交
518 519 520 521 522 523 524
			} catch (error) {
				err = error;
			}

			assert.ok(err!);
		});

B
Benjamin Pasero 已提交
525
		async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) {
526 527 528 529
			if (typeof expectedMeta === 'undefined') {
				expectedMeta = meta;
			}

B
Benjamin Pasero 已提交
530
			await service.backup(resource, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta);
531

532 533 534
			const backup = await service.resolve<IBackupTestMetaData>(resource);
			assert.ok(backup);
			assert.equal(contents, snapshotToString(backup!.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
535 536

			if (expectedMeta) {
537 538 539 540
				assert.equal(backup!.meta!.etag, expectedMeta.etag);
				assert.equal(backup!.meta!.size, expectedMeta.size);
				assert.equal(backup!.meta!.mtime, expectedMeta.mtime);
				assert.equal(backup!.meta!.orphaned, expectedMeta.orphaned);
541
			} else {
542
				assert.ok(!backup!.meta);
543 544
			}
		}
D
Daniel Imms 已提交
545
	});
D
Daniel Imms 已提交
546
});
D
Daniel Imms 已提交
547

D
Daniel Imms 已提交
548
suite('BackupFilesModel', () => {
549

550
	let service: NodeTestBackupFileService;
551 552

	setup(async () => {
553
		service = new NodeTestBackupFileService(workspaceBackupPath);
554 555 556 557 558 559 560 561 562 563 564 565

		// Delete any existing backups completely and then re-create it.
		await pfs.rimraf(backupHome, pfs.RimRafMode.MOVE);
		await pfs.mkdirp(backupHome);

		return pfs.writeFile(workspacesJsonPath, '');
	});

	teardown(() => {
		return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE);
	});

D
Daniel Imms 已提交
566
	test('simple', () => {
567
		const model = new BackupFilesModel(service.fileService);
B
Benjamin Pasero 已提交
568

B
Benjamin Pasero 已提交
569
		const resource1 = URI.file('test.html');
B
Benjamin Pasero 已提交
570 571 572 573 574 575 576 577

		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);
578
		assert.equal(model.has(resource1, 1, { foo: 'bar' }), false);
B
Benjamin Pasero 已提交
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599

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

B
Benjamin Pasero 已提交
600 601 602
		const resource2 = URI.file('test1.html');
		const resource3 = URI.file('test2.html');
		const resource4 = URI.file('test3.html');
B
Benjamin Pasero 已提交
603 604 605

		model.add(resource2);
		model.add(resource3);
606
		model.add(resource4, undefined, { foo: 'bar' });
B
Benjamin Pasero 已提交
607 608 609 610

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

B
Benjamin Pasero 已提交
612
		assert.equal(model.has(resource4), true);
613 614
		assert.equal(model.has(resource4, undefined, { foo: 'bar' }), true);
		assert.equal(model.has(resource4, undefined, { bar: 'foo' }), false);
B
Benjamin Pasero 已提交
615 616
	});

617 618 619
	test('resolve', async () => {
		await pfs.mkdirp(path.dirname(fooBackupPath));
		fs.writeFileSync(fooBackupPath, 'foo');
620
		const model = new BackupFilesModel(service.fileService);
B
Benjamin Pasero 已提交
621

B
Benjamin Pasero 已提交
622 623
		const resolvedModel = await model.resolve(URI.file(workspaceBackupPath));
		assert.equal(resolvedModel.has(URI.file(fooBackupPath)), true);
B
Benjamin Pasero 已提交
624
	});
625

D
Daniel Imms 已提交
626
	test('get', () => {
627
		const model = new BackupFilesModel(service.fileService);
628

629
		assert.deepEqual(model.get(), []);
630

B
Benjamin Pasero 已提交
631 632 633
		const file1 = URI.file('/root/file/foo.html');
		const file2 = URI.file('/root/file/bar.html');
		const untitled = URI.file('/root/untitled/bar.html');
634

D
Daniel Imms 已提交
635 636 637 638
		model.add(file1);
		model.add(file2);
		model.add(untitled);

639
		assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]);
640
	});
641
});