backupFileService.test.ts 23.0 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';
B
Benjamin Pasero 已提交
19
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
20
import { FileService } from 'vs/platform/files/common/fileService';
21
import { NullLogService } from 'vs/platform/log/common/log';
22
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
23
import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
24
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
25
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
26
import { IFileService } from 'vs/platform/files/common/files';
27
import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
28 29
import { BACKUPS } from 'vs/platform/environment/common/environment';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
B
Benjamin Pasero 已提交
30
import { VSBuffer } from 'vs/base/common/buffer';
31

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

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

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

51
	constructor(backupPath: string) {
52
		super({ ...parseArgs(process.argv, OPTIONS), ...{ backupPath, 'user-data-dir': userdataDir } } as IWindowConfiguration, process.execPath, 0);
B
Benjamin Pasero 已提交
53 54 55
	}
}

56
export class NodeTestBackupFileService extends BackupFileService {
57 58 59

	readonly fileService: IFileService;

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

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

70
		super(environmentService, fileService);
71 72

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

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

B
Benjamin Pasero 已提交
81 82
	async backup(resource: URI, content: ITextSnapshot, versionId?: number, meta?: any): Promise<void> {
		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
}

D
Daniel Imms 已提交
102
suite('BackupFileService', () => {
103
	let service: NodeTestBackupFileService;
104

105
	setup(async () => {
106
		service = new NodeTestBackupFileService(workspaceBackupPath);
107

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

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

115
	teardown(() => {
B
Benjamin Pasero 已提交
116
		return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE);
117 118
	});

R
Rob Lourens 已提交
119 120
	suite('hashPath', () => {
		test('should correctly hash the path for untitled scheme URIs', () => {
B
Benjamin Pasero 已提交
121
			const uri = URI.from({
R
Rob Lourens 已提交
122 123 124 125 126 127 128 129 130 131
				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 已提交
132
			const uri = URI.file('/foo');
R
Rob Lourens 已提交
133 134 135 136 137 138 139 140 141 142 143
			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 已提交
144 145 146 147
	suite('getBackupResource', () => {
		test('should get the correct backup path for text files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePathHash>
			const backupResource = fooFile;
148 149
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
150 151
			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 已提交
152
		});
153

D
Daniel Imms 已提交
154 155
		test('should get the correct backup path for untitled files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePath>
B
Benjamin Pasero 已提交
156
			const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
157 158
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
159 160
			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 已提交
161
		});
162 163
	});

164
	suite('backup', () => {
165
		test('text file', async () => {
B
Benjamin Pasero 已提交
166
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
167 168 169
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
			assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
170 171 172 173
			assert.ok(service.hasBackupSync(fooFile));
		});

		test('text file (with version)', async () => {
B
Benjamin Pasero 已提交
174
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), 666);
175 176 177 178 179
			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));
180 181
		});

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

190
		test('untitled file', async () => {
B
Benjamin Pasero 已提交
191
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
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`);
195
			assert.ok(service.hasBackupSync(untitledFile));
196
		});
197

198
		test('text file (ITextSnapshot)', async () => {
199 200
			const model = TextModel.createFromString('test');

B
Benjamin Pasero 已提交
201
			await service.backup(fooFile, model.createSnapshot());
202 203 204
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
			assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
205 206
			assert.ok(service.hasBackupSync(fooFile));

207
			model.dispose();
208 209
		});

210
		test('untitled file (ITextSnapshot)', async () => {
211 212
			const model = TextModel.createFromString('test');

B
Benjamin Pasero 已提交
213
			await service.backup(untitledFile, model.createSnapshot());
214 215 216
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
			assert.equal(fs.existsSync(untitledBackupPath), true);
			assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`);
217

218
			model.dispose();
219 220
		});

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

B
Benjamin Pasero 已提交
225
			await service.backup(fooFile, model.createSnapshot());
226 227 228
			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}`);
229 230
			assert.ok(service.hasBackupSync(fooFile));

231
			model.dispose();
232 233
		});

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

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

244
			model.dispose();
245
		});
246 247
	});

248
	suite('discardBackup', () => {
249
		test('text file', async () => {
B
Benjamin Pasero 已提交
250
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
251
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
252 253
			assert.ok(service.hasBackupSync(fooFile));

B
Benjamin Pasero 已提交
254
			await service.discardBackup(fooFile);
255 256
			assert.equal(fs.existsSync(fooBackupPath), false);
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0);
257
			assert.ok(!service.hasBackupSync(fooFile));
258 259
		});

260
		test('untitled file', async () => {
B
Benjamin Pasero 已提交
261
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
262
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
B
Benjamin Pasero 已提交
263
			await service.discardBackup(untitledFile);
264 265
			assert.equal(fs.existsSync(untitledBackupPath), false);
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0);
266 267
		});
	});
B
Benjamin Pasero 已提交
268

269
	suite('discardBackups', () => {
270
		test('text file', async () => {
B
Benjamin Pasero 已提交
271
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
272
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
B
Benjamin Pasero 已提交
273
			await service.backup(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
274
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2);
B
Benjamin Pasero 已提交
275
			await service.discardBackups();
276 277 278
			assert.equal(fs.existsSync(fooBackupPath), false);
			assert.equal(fs.existsSync(barBackupPath), false);
			assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false);
D
Daniel Imms 已提交
279 280
		});

281
		test('untitled file', async () => {
B
Benjamin Pasero 已提交
282
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
283
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
B
Benjamin Pasero 已提交
284
			await service.discardBackups();
285 286
			assert.equal(fs.existsSync(untitledBackupPath), false);
			assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false);
287
		});
288

289
		test('should disable further backups', async () => {
B
Benjamin Pasero 已提交
290 291
			await service.discardBackups();
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
292
			assert.equal(fs.existsSync(workspaceBackupPath), false);
293
		});
294 295
	});

296
	suite('getBackups', () => {
297
		test('("file") - text file', async () => {
B
Benjamin Pasero 已提交
298 299
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getBackups();
300
			assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]);
B
Benjamin Pasero 已提交
301 302
			await service.backup(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles_1 = await service.getBackups();
303
			assert.deepEqual(textFiles_1.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]);
D
Daniel Imms 已提交
304 305
		});

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

312
		test('("untitled") - untitled file', async () => {
B
Benjamin Pasero 已提交
313 314
			await service.backup(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getBackups();
315
			assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']);
316 317 318
		});
	});

319
	suite('resolve', () => {
320 321 322 323 324 325 326 327

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

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

331 332 333 334 335 336 337 338 339 340 341 342
			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
			};
343

344
			await testResolveBackup(untitledFile, contents, meta);
345 346
		});

347
		test('should restore the original contents (text file)', async () => {
348 349 350 351
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'consectetur ',
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 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
				'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 ',
413
				'adipiscing ßß elit',
414
				'consectetur '
415 416
			].join('');

417 418 419 420 421 422
			const meta = {
				etag: 'theEtag',
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};
423

B
Benjamin Pasero 已提交
424
			await service.backup(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta);
425 426 427 428 429 430 431 432

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

433 434 435 436
			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);
437 438
		});

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
		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);
		});

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
		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 已提交
473
		});
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492

		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 已提交
493 494 495
		test('should throw an error when restoring invalid backup', async () => {
			const contents = 'test\nand more stuff';

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

498
			const backup = await service.resolve(fooBarFile);
B
Benjamin Pasero 已提交
499 500 501 502
			if (!backup) {
				throw new Error('Unexpected missing backup');
			}

503
			await service.fileService.writeFile(service.toBackupResource(fooBarFile), VSBuffer.fromString(''));
B
Benjamin Pasero 已提交
504 505 506

			let err: Error;
			try {
507
				await service.resolve<IBackupTestMetaData>(fooBarFile);
B
Benjamin Pasero 已提交
508 509 510 511 512 513 514
			} catch (error) {
				err = error;
			}

			assert.ok(err!);
		});

B
Benjamin Pasero 已提交
515
		async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) {
516 517 518 519
			if (typeof expectedMeta === 'undefined') {
				expectedMeta = meta;
			}

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

522 523 524
			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)));
525 526

			if (expectedMeta) {
527 528 529 530
				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);
531
			} else {
532
				assert.ok(!backup!.meta);
533 534
			}
		}
D
Daniel Imms 已提交
535
	});
D
Daniel Imms 已提交
536
});
D
Daniel Imms 已提交
537

D
Daniel Imms 已提交
538
suite('BackupFilesModel', () => {
539

540
	let service: NodeTestBackupFileService;
541 542

	setup(async () => {
543
		service = new NodeTestBackupFileService(workspaceBackupPath);
544 545 546 547 548 549 550 551 552 553 554 555

		// 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 已提交
556
	test('simple', () => {
557
		const model = new BackupFilesModel(service.fileService);
B
Benjamin Pasero 已提交
558

B
Benjamin Pasero 已提交
559
		const resource1 = URI.file('test.html');
B
Benjamin Pasero 已提交
560 561 562 563 564 565 566 567

		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);
568
		assert.equal(model.has(resource1, 1, { foo: 'bar' }), false);
B
Benjamin Pasero 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589

		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 已提交
590 591 592
		const resource2 = URI.file('test1.html');
		const resource3 = URI.file('test2.html');
		const resource4 = URI.file('test3.html');
B
Benjamin Pasero 已提交
593 594 595

		model.add(resource2);
		model.add(resource3);
596
		model.add(resource4, undefined, { foo: 'bar' });
B
Benjamin Pasero 已提交
597 598 599 600

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

B
Benjamin Pasero 已提交
602
		assert.equal(model.has(resource4), true);
603 604
		assert.equal(model.has(resource4, undefined, { foo: 'bar' }), true);
		assert.equal(model.has(resource4, undefined, { bar: 'foo' }), false);
B
Benjamin Pasero 已提交
605 606
	});

607 608 609
	test('resolve', async () => {
		await pfs.mkdirp(path.dirname(fooBackupPath));
		fs.writeFileSync(fooBackupPath, 'foo');
610
		const model = new BackupFilesModel(service.fileService);
B
Benjamin Pasero 已提交
611

B
Benjamin Pasero 已提交
612 613
		const resolvedModel = await model.resolve(URI.file(workspaceBackupPath));
		assert.equal(resolvedModel.has(URI.file(fooBackupPath)), true);
B
Benjamin Pasero 已提交
614
	});
615

D
Daniel Imms 已提交
616
	test('get', () => {
617
		const model = new BackupFilesModel(service.fileService);
618

619
		assert.deepEqual(model.get(), []);
620

B
Benjamin Pasero 已提交
621 622 623
		const file1 = URI.file('/root/file/foo.html');
		const file2 = URI.file('/root/file/bar.html');
		const untitled = URI.file('/root/untitled/bar.html');
624

D
Daniel Imms 已提交
625 626 627 628
		model.add(file1);
		model.add(file2);
		model.add(untitled);

629
		assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]);
630
	});
631
});