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 } 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
	constructor(workspaceBackupPath: string) {
61
		const environmentService = new TestBackupEnvironmentService(workspaceBackupPath);
62 63 64
		const fileService = new FileService(new NullLogService());
		const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
		fileService.registerProvider(Schemas.file, diskFileSystemProvider);
S
Sandeep Somavarapu 已提交
65
		fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService));
66

67
		super(environmentService, fileService);
68 69

		this.fileService = fileService;
70 71
	}

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

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

80
	setup(async () => {
81
		service = new NodeTestBackupFileService(workspaceBackupPath);
82

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

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

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

R
Rob Lourens 已提交
94 95
	suite('hashPath', () => {
		test('should correctly hash the path for untitled scheme URIs', () => {
B
Benjamin Pasero 已提交
96
			const uri = URI.from({
R
Rob Lourens 已提交
97 98 99 100 101 102 103 104 105 106
				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 已提交
107
			const uri = URI.file('/foo');
R
Rob Lourens 已提交
108 109 110 111 112 113 114 115 116 117 118
			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 已提交
119 120 121 122
	suite('getBackupResource', () => {
		test('should get the correct backup path for text files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePathHash>
			const backupResource = fooFile;
123 124
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
125 126
			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 已提交
127
		});
128

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

139
	suite('loadBackupResource', () => {
140 141 142
		test('should return whether a backup resource exists', async () => {
			await pfs.mkdirp(path.dirname(fooBackupPath));
			fs.writeFileSync(fooBackupPath, 'foo');
143
			service = new NodeTestBackupFileService(workspaceBackupPath);
144 145 146 147 148
			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);
149
		});
D
Daniel Imms 已提交
150
	});
151

D
Daniel Imms 已提交
152
	suite('backupResource', () => {
153 154 155 156 157
		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`);
158 159 160 161 162 163 164 165 166 167
			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));
168 169
		});

170 171 172 173
		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);
174
			assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`);
175
			assert.ok(service.hasBackupSync(fooFile));
176 177
		});

178 179 180 181 182
		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`);
183
			assert.ok(service.hasBackupSync(untitledFile));
184
		});
185

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

189 190 191 192
			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`);
193 194
			assert.ok(service.hasBackupSync(fooFile));

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

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

201 202 203 204
			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`);
205

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

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

213 214 215 216
			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}`);
217 218
			assert.ok(service.hasBackupSync(fooFile));

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

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

226 227 228 229
			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}`);
230 231
			assert.ok(service.hasBackupSync(untitledFile));

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

D
Daniel Imms 已提交
236
	suite('discardResourceBackup', () => {
237 238 239
		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);
240 241
			assert.ok(service.hasBackupSync(fooFile));

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

248 249 250 251 252 253
		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);
254 255
		});
	});
B
Benjamin Pasero 已提交
256

D
Daniel Imms 已提交
257
	suite('discardAllWorkspaceBackups', () => {
258 259 260 261 262 263 264 265 266
		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 已提交
267 268
		});

269 270 271 272 273 274
		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);
275
		});
276

277 278 279 280
		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);
281
		});
282 283
	});

D
Daniel Imms 已提交
284
	suite('getWorkspaceFileBackups', () => {
285 286 287 288 289 290 291
		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 已提交
292 293
		});

294 295 296 297
		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 已提交
298 299
		});

300 301 302 303
		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']);
304 305 306
		});
	});

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

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

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

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

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

335
		test('should restore the original contents (text file)', async () => {
336 337 338 339
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'consectetur ',
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
				'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 ',
401
				'adipiscing ßß elit',
402
				'consectetur '
403 404
			].join('');

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

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

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

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

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

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
		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 已提交
462
		});
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481

		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 已提交
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
		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 已提交
504
		async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) {
505 506 507 508 509 510
			if (typeof expectedMeta === 'undefined') {
				expectedMeta = meta;
			}

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

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

			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 已提交
525
	});
D
Daniel Imms 已提交
526
});
D
Daniel Imms 已提交
527

D
Daniel Imms 已提交
528
suite('BackupFilesModel', () => {
529

530
	let service: NodeTestBackupFileService;
531 532

	setup(async () => {
533
		service = new NodeTestBackupFileService(workspaceBackupPath);
534 535 536 537 538 539 540 541 542 543 544 545

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

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

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

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

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

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

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

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

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

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

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

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

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

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