backupFileService.test.ts 23.1 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
		this.didDiscardAllBackups = false;
76 77
	}

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

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

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

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

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

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

102
	didDiscardAllBackups: boolean;
103

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

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

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

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

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

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

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

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

D
Daniel Imms 已提交
163 164
		test('should get the correct backup path for untitled files', () => {
			// Format should be: <backupHome>/<workspaceHash>/<scheme>/<filePath>
B
Benjamin Pasero 已提交
165
			const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
166 167
			const workspaceHash = hashPath(workspaceResource);
			const filePathHash = hashPath(backupResource);
168 169
			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 已提交
170
		});
171 172
	});

173
	suite('backup', () => {
174 175 176 177 178 179 180 181
		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));
		});

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

		test('text file (with version)', async () => {
B
Benjamin Pasero 已提交
191
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), 666);
192 193 194 195 196
			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));
197 198
		});

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

286
	suite('discardAllBackups', () => {
287
		test('text file', async () => {
B
Benjamin Pasero 已提交
288
			await service.backup(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
289
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1);
B
Benjamin Pasero 已提交
290
			await service.backup(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false));
291
			assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2);
292
			await service.discardAllBackups();
293 294
			assert.equal(fs.existsSync(fooBackupPath), false);
			assert.equal(fs.existsSync(barBackupPath), 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
		});
303 304
	});

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

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

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

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

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

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

340 341 342 343 344 345 346 347 348 349 350 351
			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
			};
352

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

356
		test('should restore the original contents (text file)', async () => {
357 358 359 360
			const contents = [
				'Lorem ipsum ',
				'dolor öäü sit amet ',
				'consectetur ',
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 413 414 415 416 417 418 419 420 421
				'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 ',
422
				'adipiscing ßß elit',
423
				'consectetur '
424 425
			].join('');

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

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

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

442 443 444 445
			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);
446 447
		});

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
		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);
		});

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 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 已提交
482
		});
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501

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

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

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

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

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

			assert.ok(err!);
		});

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

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

531 532 533
			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)));
534 535

			if (expectedMeta) {
536 537 538 539
				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);
540
			} else {
541
				assert.ok(!backup!.meta);
542 543
			}
		}
D
Daniel Imms 已提交
544
	});
D
Daniel Imms 已提交
545
});
D
Daniel Imms 已提交
546

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

549
	let service: NodeTestBackupFileService;
550 551

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

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

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

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

		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 已提交
599 600 601
		const resource2 = URI.file('test1.html');
		const resource3 = URI.file('test2.html');
		const resource4 = URI.file('test3.html');
B
Benjamin Pasero 已提交
602 603 604

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

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

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

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

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

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

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

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

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

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