backupFileService.test.ts 23.3 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 } 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
	}

}

57
class TestBackupFileService extends BackupFileService {
58 59 60

	readonly fileService: IFileService;

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

68
		super(environmentService, fileService);
69 70

		this.fileService = fileService;
71 72
	}

B
Benjamin Pasero 已提交
73
	toBackupResource(resource: URI): URI {
74
		return super.toBackupResource(resource);
75 76 77
	}
}

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

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

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

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

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

R
Rob Lourens 已提交
95 96
	suite('hashPath', () => {
		test('should correctly hash the path for untitled scheme URIs', () => {
B
Benjamin Pasero 已提交
97
			const uri = URI.from({
R
Rob Lourens 已提交
98 99 100 101 102 103 104 105 106 107
				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 已提交
108
			const uri = URI.file('/foo');
R
Rob Lourens 已提交
109 110 111 112 113 114 115 116 117 118 119
			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);
126 127
			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 已提交
128
		});
129

D
Daniel Imms 已提交
130 131
		test('should get the correct backup path for untitled files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePath>
B
Benjamin Pasero 已提交
132
			const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
133 134
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
135 136
			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 已提交
137
		});
138 139
	});

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

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

		test('text file (with version)', async () => {
			await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), 666);
			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));
169 170
		});

171 172 173 174
		test('text file (with meta)', async () => {
			await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), undefined, { etag: '678', orphaned: true });
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
175
			assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`);
176
			assert.ok(service.hasBackupSync(fooFile));
177 178
		});

179 180 181 182 183
		test('untitled file', async () => {
			await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
			assert.equal(fs.existsSync(untitledBackupPath), true);
			assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`);
184
			assert.ok(service.hasBackupSync(untitledFile));
185
		});
186

187
		test('text file (ITextSnapshot)', async () => {
188 189
			const model = TextModel.createFromString('test');

190 191 192 193
			await service.backupResource(fooFile, model.createSnapshot());
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			assert.equal(fs.existsSync(fooBackupPath), true);
			assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`);
194 195
			assert.ok(service.hasBackupSync(fooFile));

196
			model.dispose();
197 198
		});

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

202 203 204 205
			await service.backupResource(untitledFile, model.createSnapshot());
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
			assert.equal(fs.existsSync(untitledBackupPath), true);
			assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`);
206

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

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

214 215 216 217
			await service.backupResource(fooFile, model.createSnapshot());
			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}`);
218 219
			assert.ok(service.hasBackupSync(fooFile));

220
			model.dispose();
221 222
		});

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

227 228 229 230
			await service.backupResource(untitledFile, model.createSnapshot());
			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}`);
231 232
			assert.ok(service.hasBackupSync(untitledFile));

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

D
Daniel Imms 已提交
237
	suite('discardResourceBackup', () => {
238 239 240
		test('text file', async () => {
			await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
241 242
			assert.ok(service.hasBackupSync(fooFile));

243 244 245
			await service.discardResourceBackup(fooFile);
			assert.equal(fs.existsSync(fooBackupPath), false);
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0);
246
			assert.ok(!service.hasBackupSync(fooFile));
247 248
		});

249 250 251 252 253 254
		test('untitled file', async () => {
			await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
			await service.discardResourceBackup(untitledFile);
			assert.equal(fs.existsSync(untitledBackupPath), false);
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0);
255 256
		});
	});
B
Benjamin Pasero 已提交
257

D
Daniel Imms 已提交
258
	suite('discardAllWorkspaceBackups', () => {
259 260 261 262 263 264 265 266 267
		test('text file', async () => {
			await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
			await service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2);
			await service.discardAllWorkspaceBackups();
			assert.equal(fs.existsSync(fooBackupPath), false);
			assert.equal(fs.existsSync(barBackupPath), false);
			assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false);
D
Daniel Imms 已提交
268 269
		});

270 271 272 273 274 275
		test('untitled file', async () => {
			await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1);
			await service.discardAllWorkspaceBackups();
			assert.equal(fs.existsSync(untitledBackupPath), false);
			assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false);
276
		});
277

278 279 280 281
		test('should disable further backups', async () => {
			await service.discardAllWorkspaceBackups();
			await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			assert.equal(fs.existsSync(workspaceBackupPath), false);
282
		});
283 284
	});

D
Daniel Imms 已提交
285
	suite('getWorkspaceFileBackups', () => {
286 287 288 289 290 291 292
		test('("file") - text file', async () => {
			await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getWorkspaceFileBackups();
			assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]);
			await service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles_1 = await service.getWorkspaceFileBackups();
			assert.deepEqual(textFiles_1.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]);
D
Daniel Imms 已提交
293 294
		});

295 296 297 298
		test('("file") - untitled file', async () => {
			await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getWorkspaceFileBackups();
			assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]);
D
Daniel Imms 已提交
299 300
		});

301 302 303 304
		test('("untitled") - untitled file', async () => {
			await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
			const textFiles = await service.getWorkspaceFileBackups();
			assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']);
305 306 307
		});
	});

308 309 310 311 312 313 314 315 316
	suite('resolveBackupContent', () => {

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

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

320 321 322 323 324 325 326 327 328 329 330 331
			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
			};
332

333
			await testResolveBackup(untitledFile, contents, meta);
334 335
		});

336
		test('should restore the original contents (text file)', async () => {
337 338 339 340
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'consectetur ',
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 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
				'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 ',
402
				'adipiscing ßß elit',
403
				'consectetur '
404 405
			].join('');

406 407 408 409 410 411
			const meta = {
				etag: 'theEtag',
				size: 888,
				mtime: Date.now(),
				orphaned: false
			};
412

413 414
			await service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta);

415 416
			assert.ok(await service.loadBackupResource(fooFile));

417 418 419 420 421 422 423 424 425 426 427 428
			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);

			const backup = await service.resolveBackupContent(service.toBackupResource(fooFile));
			assert.equal(contents, snapshotToString(backup.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
			assert.ok(!backup.meta);
		});

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
		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);
		});

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
		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 已提交
463
		});
464 465 466 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 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 已提交
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
		test('should throw an error when restoring invalid backup', async () => {
			const contents = 'test\nand more stuff';

			await service.backupResource(fooBarFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1);

			const backup = await service.loadBackupResource(fooBarFile);
			if (!backup) {
				throw new Error('Unexpected missing backup');
			}

			await service.fileService.writeFile(backup, VSBuffer.fromString(''));

			let err: Error;
			try {
				await service.resolveBackupContent<IBackupTestMetaData>(backup);
			} catch (error) {
				err = error;
			}

			assert.ok(err!);
		});

B
Benjamin Pasero 已提交
505
		async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) {
506 507 508 509 510 511
			if (typeof expectedMeta === 'undefined') {
				expectedMeta = meta;
			}

			await service.backupResource(resource, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta);

512
			assert.ok(await service.loadBackupResource(resource));
513 514 515 516 517 518 519 520 521 522 523 524 525

			const backup = await service.resolveBackupContent<IBackupTestMetaData>(service.toBackupResource(resource));
			assert.equal(contents, snapshotToString(backup.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));

			if (expectedMeta) {
				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);
			} else {
				assert.ok(!backup.meta);
			}
		}
D
Daniel Imms 已提交
526
	});
D
Daniel Imms 已提交
527
});
D
Daniel Imms 已提交
528

D
Daniel Imms 已提交
529
suite('BackupFilesModel', () => {
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546

	let service: TestBackupFileService;

	setup(async () => {
		service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);

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

B
Benjamin Pasero 已提交
550
		const resource1 = URI.file('test.html');
B
Benjamin Pasero 已提交
551 552 553 554 555 556 557 558

		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);
559
		assert.equal(model.has(resource1, 1, { foo: 'bar' }), false);
B
Benjamin Pasero 已提交
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580

		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 已提交
581 582 583
		const resource2 = URI.file('test1.html');
		const resource3 = URI.file('test2.html');
		const resource4 = URI.file('test3.html');
B
Benjamin Pasero 已提交
584 585 586

		model.add(resource2);
		model.add(resource3);
587
		model.add(resource4, undefined, { foo: 'bar' });
B
Benjamin Pasero 已提交
588 589 590 591

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

B
Benjamin Pasero 已提交
593
		assert.equal(model.has(resource4), true);
594 595
		assert.equal(model.has(resource4, undefined, { foo: 'bar' }), true);
		assert.equal(model.has(resource4, undefined, { bar: 'foo' }), false);
B
Benjamin Pasero 已提交
596 597
	});

598 599 600
	test('resolve', async () => {
		await pfs.mkdirp(path.dirname(fooBackupPath));
		fs.writeFileSync(fooBackupPath, 'foo');
601
		const model = new BackupFilesModel(service.fileService);
B
Benjamin Pasero 已提交
602

B
Benjamin Pasero 已提交
603 604
		const resolvedModel = await model.resolve(URI.file(workspaceBackupPath));
		assert.equal(resolvedModel.has(URI.file(fooBackupPath)), true);
B
Benjamin Pasero 已提交
605
	});
606

D
Daniel Imms 已提交
607
	test('get', () => {
608
		const model = new BackupFilesModel(service.fileService);
609

610
		assert.deepEqual(model.get(), []);
611

B
Benjamin Pasero 已提交
612 613 614
		const file1 = URI.file('/root/file/foo.html');
		const file2 = URI.file('/root/file/bar.html');
		const untitled = URI.file('/root/untitled/bar.html');
615

D
Daniel Imms 已提交
616 617 618 619
		model.add(file1);
		model.add(file2);
		model.add(untitled);

620
		assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]);
621
	});
622
});