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, 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);
295
			assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false);
D
Daniel Imms 已提交
296 297
		});

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

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

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

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

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

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

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

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

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

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

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

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

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

444 445 446 447
			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);
448 449
		});

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

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
		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 已提交
484
		});
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503

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

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

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

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

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

			assert.ok(err!);
		});

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

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

533 534 535
			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)));
536 537

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

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

551
	let service: NodeTestBackupFileService;
552 553

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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