diskFileService.test.ts 69.8 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  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';
import { tmpdir } from 'os';
8
import { FileService } from 'vs/platform/files/common/fileService';
9
import { Schemas } from 'vs/base/common/network';
10
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
11 12
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { generateUuid } from 'vs/base/common/uuid';
13
import { join, basename, dirname, posix } from 'vs/base/common/path';
14
import { getPathFromAmdModule } from 'vs/base/common/amd';
15
import { copy, rimraf, symlink, RimRafMode, rimrafSync } from 'vs/base/node/pfs';
16
import { URI } from 'vs/base/common/uri';
17
import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, createReadStream } from 'fs';
18
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata } from 'vs/platform/files/common/files';
B
Benjamin Pasero 已提交
19
import { NullLogService } from 'vs/platform/log/common/log';
20
import { isLinux, isWindows } from 'vs/base/common/platform';
21
import { DisposableStore } from 'vs/base/common/lifecycle';
B
Benjamin Pasero 已提交
22
import { isEqual } from 'vs/base/common/resources';
23
import { VSBuffer, VSBufferReadable, toVSBufferReadableStream, VSBufferReadableStream, bufferToReadable, bufferToStream } from 'vs/base/common/buffer';
B
Benjamin Pasero 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37

function getByName(root: IFileStat, name: string): IFileStat | null {
	if (root.children === undefined) {
		return null;
	}

	for (const child of root.children) {
		if (child.name === name) {
			return child;
		}
	}

	return null;
}
38

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
function toLineByLineReadable(content: string): VSBufferReadable {
	let chunks = content.split('\n');
	chunks = chunks.map((chunk, index) => {
		if (index === 0) {
			return chunk;
		}

		return '\n' + chunk;
	});

	return {
		read(): VSBuffer | null {
			const chunk = chunks.shift();
			if (typeof chunk === 'string') {
				return VSBuffer.fromString(chunk);
			}

			return null;
		}
	};
}

61 62
export class TestDiskFileSystemProvider extends DiskFileSystemProvider {

63 64
	totalBytesRead: number = 0;

B
Benjamin Pasero 已提交
65 66
	private invalidStatSize: boolean;

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	private _testCapabilities: FileSystemProviderCapabilities;
	get capabilities(): FileSystemProviderCapabilities {
		if (!this._testCapabilities) {
			this._testCapabilities =
				FileSystemProviderCapabilities.FileReadWrite |
				FileSystemProviderCapabilities.FileOpenReadWriteClose |
				FileSystemProviderCapabilities.FileFolderCopy;

			if (isLinux) {
				this._testCapabilities |= FileSystemProviderCapabilities.PathCaseSensitive;
			}
		}

		return this._testCapabilities;
	}

	set capabilities(capabilities: FileSystemProviderCapabilities) {
		this._testCapabilities = capabilities;
	}
86

B
Benjamin Pasero 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100
	setInvalidStatSize(disabled: boolean): void {
		this.invalidStatSize = disabled;
	}

	async stat(resource: URI): Promise<IStat> {
		const res = await super.stat(resource);

		if (this.invalidStatSize) {
			res.size = String(res.size) as any; // for https://github.com/Microsoft/vscode/issues/72909
		}

		return res;
	}

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
		const bytesRead = await super.read(fd, pos, data, offset, length);

		this.totalBytesRead += bytesRead;

		return bytesRead;
	}

	async readFile(resource: URI): Promise<Uint8Array> {
		const res = await super.readFile(resource);

		this.totalBytesRead += res.byteLength;

		return res;
	}
116 117
}

B
Benjamin Pasero 已提交
118
suite('Disk File Service', function () {
119 120

	const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'diskfileservice');
121
	const testSchema = 'test';
122

B
Benjamin Pasero 已提交
123
	let service: FileService;
124 125
	let fileProvider: TestDiskFileSystemProvider;
	let testProvider: TestDiskFileSystemProvider;
126 127
	let testDir: string;

128
	const disposables = new DisposableStore();
129

B
Benjamin Pasero 已提交
130 131 132 133 134 135
	// Given issues such as https://github.com/microsoft/vscode/issues/78602
	// we see random test failures when accessing the native file system. To
	// diagnose further, we retry node.js file access tests up to 3 times to
	// rule out any random disk issue.
	this.retries(3);

136
	setup(async () => {
B
Benjamin Pasero 已提交
137 138
		const logService = new NullLogService();

B
Benjamin Pasero 已提交
139
		service = new FileService(logService);
140
		disposables.add(service);
141

B
Benjamin Pasero 已提交
142
		fileProvider = new TestDiskFileSystemProvider(logService);
143 144
		disposables.add(service.registerProvider(Schemas.file, fileProvider));
		disposables.add(fileProvider);
145

B
Benjamin Pasero 已提交
146
		testProvider = new TestDiskFileSystemProvider(logService);
147 148
		disposables.add(service.registerProvider(testSchema, testProvider));
		disposables.add(testProvider);
149 150 151 152 153 154 155 156 157

		const id = generateUuid();
		testDir = join(parentDir, id);
		const sourceDir = getPathFromAmdModule(require, './fixtures/service');

		await copy(sourceDir, testDir);
	});

	teardown(async () => {
158
		disposables.clear();
159

B
Benjamin Pasero 已提交
160
		await rimraf(parentDir, RimRafMode.MOVE);
161 162 163
	});

	test('createFolder', async () => {
B
Benjamin Pasero 已提交
164
		let event: FileOperationEvent | undefined;
165
		disposables.add(service.onAfterOperation(e => event = e));
166

B
Benjamin Pasero 已提交
167
		const parent = await service.resolve(URI.file(testDir));
168

169
		const newFolderResource = URI.file(join(parent.resource.fsPath, 'newFolder'));
170

171
		const newFolder = await service.createFolder(newFolderResource);
B
Benjamin Pasero 已提交
172

173 174
		assert.equal(newFolder.name, 'newFolder');
		assert.equal(existsSync(newFolder.resource.fsPath), true);
B
Benjamin Pasero 已提交
175 176

		assert.ok(event);
177
		assert.equal(event!.resource.fsPath, newFolderResource.fsPath);
B
Benjamin Pasero 已提交
178
		assert.equal(event!.operation, FileOperation.CREATE);
179
		assert.equal(event!.target!.resource.fsPath, newFolderResource.fsPath);
B
Benjamin Pasero 已提交
180
		assert.equal(event!.target!.isDirectory, true);
181 182
	});

B
Benjamin Pasero 已提交
183
	test('createFolder: creating multiple folders at once', async function () {
184
		let event: FileOperationEvent;
185
		disposables.add(service.onAfterOperation(e => event = e));
186 187

		const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
B
Benjamin Pasero 已提交
188
		const parent = await service.resolve(URI.file(testDir));
B
Benjamin Pasero 已提交
189

190
		const newFolderResource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths));
B
Benjamin Pasero 已提交
191

192
		const newFolder = await service.createFolder(newFolderResource);
B
Benjamin Pasero 已提交
193 194

		const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1];
195 196
		assert.equal(newFolder.name, lastFolderName);
		assert.equal(existsSync(newFolder.resource.fsPath), true);
B
Benjamin Pasero 已提交
197 198

		assert.ok(event!);
199
		assert.equal(event!.resource.fsPath, newFolderResource.fsPath);
B
Benjamin Pasero 已提交
200
		assert.equal(event!.operation, FileOperation.CREATE);
201
		assert.equal(event!.target!.resource.fsPath, newFolderResource.fsPath);
B
Benjamin Pasero 已提交
202 203 204
		assert.equal(event!.target!.isDirectory, true);
	});

B
Benjamin Pasero 已提交
205 206
	test('exists', async () => {
		let exists = await service.exists(URI.file(testDir));
B
Benjamin Pasero 已提交
207 208
		assert.equal(exists, true);

B
Benjamin Pasero 已提交
209
		exists = await service.exists(URI.file(testDir + 'something'));
B
Benjamin Pasero 已提交
210 211 212
		assert.equal(exists, false);
	});

B
Benjamin Pasero 已提交
213 214
	test('resolve', async () => {
		const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });
B
Benjamin Pasero 已提交
215 216 217 218 219 220
		assert.equal(resolved.children!.length, 8);

		const deep = (getByName(resolved, 'deep')!);
		assert.equal(deep.children!.length, 4);
	});

B
Benjamin Pasero 已提交
221
	test('resolve - directory', async () => {
B
Benjamin Pasero 已提交
222 223
		const testsElements = ['examples', 'other', 'index.html', 'site.css'];

B
Benjamin Pasero 已提交
224
		const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')));
B
Benjamin Pasero 已提交
225 226 227 228 229 230 231

		assert.ok(result);
		assert.ok(result.children);
		assert.ok(result.children!.length > 0);
		assert.ok(result!.isDirectory);
		assert.equal(result.children!.length, testsElements.length);

232 233
		assert.ok(result.children!.every(entry => {
			return testsElements.some(name => {
B
Benjamin Pasero 已提交
234
				return basename(entry.resource.fsPath) === name;
235
			});
B
Benjamin Pasero 已提交
236 237
		}));

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
		result.children!.forEach(value => {
			assert.ok(basename(value.resource.fsPath));
			if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
				assert.ok(value.isDirectory);
			} else if (basename(value.resource.fsPath) === 'index.html') {
				assert.ok(!value.isDirectory);
				assert.ok(!value.children);
			} else if (basename(value.resource.fsPath) === 'site.css') {
				assert.ok(!value.isDirectory);
				assert.ok(!value.children);
			} else {
				assert.ok(!'Unexpected value ' + basename(value.resource.fsPath));
			}
		});
	});

B
Benjamin Pasero 已提交
254
	test('resolve - directory - with metadata', async () => {
255 256
		const testsElements = ['examples', 'other', 'index.html', 'site.css'];

B
Benjamin Pasero 已提交
257
		const result = await service.resolve(URI.file(getPathFromAmdModule(require, './fixtures/resolver')), { resolveMetadata: true });
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273

		assert.ok(result);
		assert.ok(result.children);
		assert.ok(result.children!.length > 0);
		assert.ok(result!.isDirectory);
		assert.equal(result.children!.length, testsElements.length);

		assert.ok(result.children!.every(entry => {
			return testsElements.some(name => {
				return basename(entry.resource.fsPath) === name;
			});
		}));

		assert.ok(result.children!.every(entry => entry.etag.length > 0));

		result.children!.forEach(value => {
B
Benjamin Pasero 已提交
274 275 276 277 278 279 280 281 282 283 284 285
			assert.ok(basename(value.resource.fsPath));
			if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {
				assert.ok(value.isDirectory);
			} else if (basename(value.resource.fsPath) === 'index.html') {
				assert.ok(!value.isDirectory);
				assert.ok(!value.children);
			} else if (basename(value.resource.fsPath) === 'site.css') {
				assert.ok(!value.isDirectory);
				assert.ok(!value.children);
			} else {
				assert.ok(!'Unexpected value ' + basename(value.resource.fsPath));
			}
286 287
		});
	});
288

B
Benjamin Pasero 已提交
289
	test('resolve - directory - resolveTo single directory', async () => {
B
Benjamin Pasero 已提交
290
		const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
B
Benjamin Pasero 已提交
291
		const result = await service.resolve(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] });
292

B
Benjamin Pasero 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
		assert.ok(result);
		assert.ok(result.children);
		assert.ok(result.children!.length > 0);
		assert.ok(result.isDirectory);

		const children = result.children!;
		assert.equal(children.length, 4);

		const other = getByName(result, 'other');
		assert.ok(other);
		assert.ok(other!.children!.length > 0);

		const deep = getByName(other!, 'deep');
		assert.ok(deep);
		assert.ok(deep!.children!.length > 0);
		assert.equal(deep!.children!.length, 4);
	});

	test('resolve directory - resolveTo multiple directories', async () => {
		const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
B
Benjamin Pasero 已提交
313
		const result = await service.resolve(URI.file(resolverFixturesPath), {
B
Benjamin Pasero 已提交
314 315 316 317
			resolveTo: [
				URI.file(join(resolverFixturesPath, 'other/deep')),
				URI.file(join(resolverFixturesPath, 'examples'))
			]
318
		});
B
Benjamin Pasero 已提交
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

		assert.ok(result);
		assert.ok(result.children);
		assert.ok(result.children!.length > 0);
		assert.ok(result.isDirectory);

		const children = result.children!;
		assert.equal(children.length, 4);

		const other = getByName(result, 'other');
		assert.ok(other);
		assert.ok(other!.children!.length > 0);

		const deep = getByName(other!, 'deep');
		assert.ok(deep);
		assert.ok(deep!.children!.length > 0);
		assert.equal(deep!.children!.length, 4);

		const examples = getByName(result, 'examples');
		assert.ok(examples);
		assert.ok(examples!.children!.length > 0);
		assert.equal(examples!.children!.length, 4);
	});

	test('resolve directory - resolveSingleChildFolders', async () => {
		const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver/other');
B
Benjamin Pasero 已提交
345
		const result = await service.resolve(URI.file(resolverFixturesPath), { resolveSingleChildDescendants: true });
B
Benjamin Pasero 已提交
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

		assert.ok(result);
		assert.ok(result.children);
		assert.ok(result.children!.length > 0);
		assert.ok(result.isDirectory);

		const children = result.children!;
		assert.equal(children.length, 1);

		let deep = getByName(result, 'deep');
		assert.ok(deep);
		assert.ok(deep!.children!.length > 0);
		assert.equal(deep!.children!.length, 4);
	});

B
Benjamin Pasero 已提交
361 362
	test('resolves', async () => {
		const res = await service.resolveAll([
B
Benjamin Pasero 已提交
363 364 365 366 367 368 369 370 371 372 373 374 375
			{ resource: URI.file(testDir), options: { resolveTo: [URI.file(join(testDir, 'deep'))] } },
			{ resource: URI.file(join(testDir, 'deep')) }
		]);

		const r1 = (res[0].stat!);
		assert.equal(r1.children!.length, 8);

		const deep = (getByName(r1, 'deep')!);
		assert.equal(deep.children!.length, 4);

		const r2 = (res[1].stat!);
		assert.equal(r2.children!.length, 4);
		assert.equal(r2.name, 'deep');
376
	});
377

B
Benjamin Pasero 已提交
378
	test('resolve - folder symbolic link', async () => {
379
		if (isWindows) {
380
			return; // not reliable on windows
381 382
		}

383
		const link = URI.file(join(testDir, 'deep-link'));
384
		await symlink(join(testDir, 'deep'), link.fsPath);
385

B
Benjamin Pasero 已提交
386
		const resolved = await service.resolve(link);
387 388 389 390 391
		assert.equal(resolved.children!.length, 4);
		assert.equal(resolved.isDirectory, true);
		assert.equal(resolved.isSymbolicLink, true);
	});

B
Benjamin Pasero 已提交
392
	test('resolve - file symbolic link', async () => {
393
		if (isWindows) {
394
			return; // not reliable on windows
395 396 397
		}

		const link = URI.file(join(testDir, 'lorem.txt-linked'));
398
		await symlink(join(testDir, 'lorem.txt'), link.fsPath);
399

B
Benjamin Pasero 已提交
400
		const resolved = await service.resolve(link);
401 402 403 404
		assert.equal(resolved.isDirectory, false);
		assert.equal(resolved.isSymbolicLink, true);
	});

B
Benjamin Pasero 已提交
405
	test('resolve - invalid symbolic link does not break', async () => {
406
		if (isWindows) {
407
			return; // not reliable on windows
408 409
		}

410
		const link = URI.file(join(testDir, 'foo'));
411
		await symlink(link.fsPath, join(testDir, 'bar'));
B
Benjamin Pasero 已提交
412

B
Benjamin Pasero 已提交
413
		const resolved = await service.resolve(URI.file(testDir));
B
Benjamin Pasero 已提交
414 415 416 417
		assert.equal(resolved.isDirectory, true);
		assert.equal(resolved.children!.length, 8);
	});

418 419
	test('deleteFile', async () => {
		let event: FileOperationEvent;
420
		disposables.add(service.onAfterOperation(e => event = e));
421 422

		const resource = URI.file(join(testDir, 'deep', 'conway.js'));
B
Benjamin Pasero 已提交
423
		const source = await service.resolve(resource);
424 425 426 427 428 429 430 431 432 433 434

		await service.del(source.resource);

		assert.equal(existsSync(source.resource.fsPath), false);
		assert.ok(event!);
		assert.equal(event!.resource.fsPath, resource.fsPath);
		assert.equal(event!.operation, FileOperation.DELETE);
	});

	test('deleteFolder (recursive)', async () => {
		let event: FileOperationEvent;
435
		disposables.add(service.onAfterOperation(e => event = e));
436 437

		const resource = URI.file(join(testDir, 'deep'));
B
Benjamin Pasero 已提交
438
		const source = await service.resolve(resource);
439 440 441 442 443 444 445 446 447 448 449

		await service.del(source.resource, { recursive: true });

		assert.equal(existsSync(source.resource.fsPath), false);
		assert.ok(event!);
		assert.equal(event!.resource.fsPath, resource.fsPath);
		assert.equal(event!.operation, FileOperation.DELETE);
	});

	test('deleteFolder (non recursive)', async () => {
		const resource = URI.file(join(testDir, 'deep'));
B
Benjamin Pasero 已提交
450
		const source = await service.resolve(resource);
451 452

		let error;
453 454
		try {
			await service.del(source.resource);
455 456
		} catch (e) {
			error = e;
457
		}
458 459

		assert.ok(error);
460
	});
B
Benjamin Pasero 已提交
461

B
Benjamin Pasero 已提交
462
	test('move', async () => {
B
Benjamin Pasero 已提交
463
		let event: FileOperationEvent;
464
		disposables.add(service.onAfterOperation(e => event = e));
B
Benjamin Pasero 已提交
465

466 467
		const source = URI.file(join(testDir, 'index.html'));
		const sourceContents = readFileSync(source.fsPath);
B
Benjamin Pasero 已提交
468

469 470
		const target = URI.file(join(dirname(source.fsPath), 'other.html'));

B
Benjamin Pasero 已提交
471
		const renamed = await service.move(source, target);
B
Benjamin Pasero 已提交
472 473

		assert.equal(existsSync(renamed.resource.fsPath), true);
474
		assert.equal(existsSync(source.fsPath), false);
B
Benjamin Pasero 已提交
475
		assert.ok(event!);
476
		assert.equal(event!.resource.fsPath, source.fsPath);
B
Benjamin Pasero 已提交
477 478 479
		assert.equal(event!.operation, FileOperation.MOVE);
		assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);

480 481 482 483 484 485
		const targetContents = readFileSync(target.fsPath);

		assert.equal(sourceContents.byteLength, targetContents.byteLength);
		assert.equal(sourceContents.toString(), targetContents.toString());
	});

B
Benjamin Pasero 已提交
486
	test('move - across providers (buffered => buffered)', async () => {
487 488 489 490 491 492
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		await testMoveAcrossProviders();
	});

B
Benjamin Pasero 已提交
493
	test('move - across providers (unbuffered => unbuffered)', async () => {
494 495 496 497 498 499
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);

		await testMoveAcrossProviders();
	});

B
Benjamin Pasero 已提交
500
	test('move - across providers (buffered => unbuffered)', async () => {
501 502 503 504 505 506
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);

		await testMoveAcrossProviders();
	});

B
Benjamin Pasero 已提交
507
	test('move - across providers (unbuffered => buffered)', async () => {
508 509 510 511
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		await testMoveAcrossProviders();
B
Benjamin Pasero 已提交
512 513
	});

B
Benjamin Pasero 已提交
514
	test('move - across providers - large (buffered => buffered)', async () => {
515 516 517 518 519 520
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		await testMoveAcrossProviders('lorem.txt');
	});

B
Benjamin Pasero 已提交
521
	test('move - across providers - large (unbuffered => unbuffered)', async () => {
522 523 524 525 526 527
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);

		await testMoveAcrossProviders('lorem.txt');
	});

B
Benjamin Pasero 已提交
528
	test('move - across providers - large (buffered => unbuffered)', async () => {
529 530 531 532 533 534
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);

		await testMoveAcrossProviders('lorem.txt');
	});

B
Benjamin Pasero 已提交
535
	test('move - across providers - large (unbuffered => buffered)', async () => {
536 537 538 539 540 541 542
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		await testMoveAcrossProviders('lorem.txt');
	});

	async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> {
B
Benjamin Pasero 已提交
543
		let event: FileOperationEvent;
544
		disposables.add(service.onAfterOperation(e => event = e));
B
Benjamin Pasero 已提交
545

546 547
		const source = URI.file(join(testDir, sourceFile));
		const sourceContents = readFileSync(source.fsPath);
B
Benjamin Pasero 已提交
548

549
		const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema });
B
Benjamin Pasero 已提交
550

B
Benjamin Pasero 已提交
551
		const renamed = await service.move(source, target);
B
Benjamin Pasero 已提交
552 553

		assert.equal(existsSync(renamed.resource.fsPath), true);
554
		assert.equal(existsSync(source.fsPath), false);
B
Benjamin Pasero 已提交
555
		assert.ok(event!);
556
		assert.equal(event!.resource.fsPath, source.fsPath);
557
		assert.equal(event!.operation, FileOperation.COPY);
B
Benjamin Pasero 已提交
558 559
		assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);

560
		const targetContents = readFileSync(target.fsPath);
B
Benjamin Pasero 已提交
561

562 563 564 565
		assert.equal(sourceContents.byteLength, targetContents.byteLength);
		assert.equal(sourceContents.toString(), targetContents.toString());
	}

B
Benjamin Pasero 已提交
566
	test('move - multi folder', async () => {
B
Benjamin Pasero 已提交
567
		let event: FileOperationEvent;
568
		disposables.add(service.onAfterOperation(e => event = e));
B
Benjamin Pasero 已提交
569

570 571 572 573
		const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
		const renameToPath = join(...multiFolderPaths, 'other.html');

		const source = URI.file(join(testDir, 'index.html'));
B
Benjamin Pasero 已提交
574

B
Benjamin Pasero 已提交
575
		const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), renameToPath)));
B
Benjamin Pasero 已提交
576 577

		assert.equal(existsSync(renamed.resource.fsPath), true);
578
		assert.equal(existsSync(source.fsPath), false);
B
Benjamin Pasero 已提交
579
		assert.ok(event!);
580
		assert.equal(event!.resource.fsPath, source.fsPath);
B
Benjamin Pasero 已提交
581 582 583 584
		assert.equal(event!.operation, FileOperation.MOVE);
		assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
	});

B
Benjamin Pasero 已提交
585
	test('move - directory', async () => {
B
Benjamin Pasero 已提交
586
		let event: FileOperationEvent;
587
		disposables.add(service.onAfterOperation(e => event = e));
B
Benjamin Pasero 已提交
588

589
		const source = URI.file(join(testDir, 'deep'));
B
Benjamin Pasero 已提交
590

B
Benjamin Pasero 已提交
591
		const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'deeper')));
B
Benjamin Pasero 已提交
592 593

		assert.equal(existsSync(renamed.resource.fsPath), true);
594
		assert.equal(existsSync(source.fsPath), false);
B
Benjamin Pasero 已提交
595
		assert.ok(event!);
596
		assert.equal(event!.resource.fsPath, source.fsPath);
B
Benjamin Pasero 已提交
597 598
		assert.equal(event!.operation, FileOperation.MOVE);
		assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
599 600
	});

B
Benjamin Pasero 已提交
601
	test('move - directory - across providers (buffered => buffered)', async () => {
602 603
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
B
Benjamin Pasero 已提交
604

605
		await testMoveFolderAcrossProviders();
B
Benjamin Pasero 已提交
606 607
	});

B
Benjamin Pasero 已提交
608
	test('move - directory - across providers (unbuffered => unbuffered)', async () => {
609 610 611 612
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);

		await testMoveFolderAcrossProviders();
B
Benjamin Pasero 已提交
613 614
	});

B
Benjamin Pasero 已提交
615
	test('move - directory - across providers (buffered => unbuffered)', async () => {
616 617 618 619 620 621
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);

		await testMoveFolderAcrossProviders();
	});

J
Joao Moreno 已提交
622
	test('move - directory - across providers (unbuffered => buffered)', async function () {
623 624 625 626 627 628 629
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
		setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		await testMoveFolderAcrossProviders();
	});

	async function testMoveFolderAcrossProviders(): Promise<void> {
B
Benjamin Pasero 已提交
630
		let event: FileOperationEvent;
631
		disposables.add(service.onAfterOperation(e => event = e));
B
Benjamin Pasero 已提交
632

633 634
		const source = URI.file(join(testDir, 'deep'));
		const sourceChildren = readdirSync(source.fsPath);
B
Benjamin Pasero 已提交
635

636 637
		const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema });

B
Benjamin Pasero 已提交
638
		const renamed = await service.move(source, target);
B
Benjamin Pasero 已提交
639 640

		assert.equal(existsSync(renamed.resource.fsPath), true);
641
		assert.equal(existsSync(source.fsPath), false);
B
Benjamin Pasero 已提交
642
		assert.ok(event!);
643
		assert.equal(event!.resource.fsPath, source.fsPath);
644
		assert.equal(event!.operation, FileOperation.COPY);
B
Benjamin Pasero 已提交
645 646
		assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);

647 648 649 650 651 652 653
		const targetChildren = readdirSync(target.fsPath);
		assert.equal(sourceChildren.length, targetChildren.length);
		for (let i = 0; i < sourceChildren.length; i++) {
			assert.equal(sourceChildren[i], targetChildren[i]);
		}
	}

B
Benjamin Pasero 已提交
654
	test('move - MIX CASE', async () => {
655
		let event: FileOperationEvent;
656
		disposables.add(service.onAfterOperation(e => event = e));
657

658 659
		const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		assert.ok(source.size > 0);
660

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
		const renamedResource = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));
		let renamed = await service.move(source.resource, renamedResource);

		assert.equal(existsSync(renamedResource.fsPath), true);
		assert.equal(basename(renamedResource.fsPath), 'INDEX.html');
		assert.ok(event!);
		assert.equal(event!.resource.fsPath, source.resource.fsPath);
		assert.equal(event!.operation, FileOperation.MOVE);
		assert.equal(event!.target!.resource.fsPath, renamedResource.fsPath);

		renamed = await service.resolve(renamedResource, { resolveMetadata: true });
		assert.equal(source.size, renamed.size);
	});

	test('move - same file', async () => {
		let event: FileOperationEvent;
		disposables.add(service.onAfterOperation(e => event = e));

		const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		assert.ok(source.size > 0);

		let renamed = await service.move(source.resource, URI.file(source.resource.fsPath));
683 684

		assert.equal(existsSync(renamed.resource.fsPath), true);
685
		assert.equal(basename(renamed.resource.fsPath), 'index.html');
686
		assert.ok(event!);
687
		assert.equal(event!.resource.fsPath, source.resource.fsPath);
688 689
		assert.equal(event!.operation, FileOperation.MOVE);
		assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);
690 691 692

		renamed = await service.resolve(renamed.resource, { resolveMetadata: true });
		assert.equal(source.size, renamed.size);
B
Benjamin Pasero 已提交
693 694
	});

695 696 697
	test('move - same file #2', async () => {
		let event: FileOperationEvent;
		disposables.add(service.onAfterOperation(e => event = e));
698

699 700
		const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		assert.ok(source.size > 0);
701

702 703
		const targetParent = URI.file(testDir);
		const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });
704

705
		let renamed = await service.move(source.resource, target);
B
Benjamin Pasero 已提交
706

707 708 709 710 711 712 713 714 715
		assert.equal(existsSync(renamed.resource.fsPath), true);
		assert.equal(basename(renamed.resource.fsPath), 'index.html');
		assert.ok(event!);
		assert.equal(event!.resource.fsPath, source.resource.fsPath);
		assert.equal(event!.operation, FileOperation.MOVE);
		assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath);

		renamed = await service.resolve(renamed.resource, { resolveMetadata: true });
		assert.equal(source.size, renamed.size);
716 717
	});

B
Benjamin Pasero 已提交
718
	test('move - source parent of target', async () => {
B
Benjamin Pasero 已提交
719
		let event: FileOperationEvent;
720
		disposables.add(service.onAfterOperation(e => event = e));
B
Benjamin Pasero 已提交
721

722 723 724 725 726
		let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		const originalSize = source.size;
		assert.ok(originalSize > 0);

		let error;
B
Benjamin Pasero 已提交
727
		try {
B
Benjamin Pasero 已提交
728
			await service.move(URI.file(testDir), URI.file(join(testDir, 'binary.txt')));
B
Benjamin Pasero 已提交
729
		} catch (e) {
730
			error = e;
B
Benjamin Pasero 已提交
731
		}
732 733 734 735 736 737

		assert.ok(error);
		assert.ok(!event!);

		source = await service.resolve(source.resource, { resolveMetadata: true });
		assert.equal(originalSize, source.size);
B
Benjamin Pasero 已提交
738 739
	});

B
Benjamin Pasero 已提交
740
	test('move - FILE_MOVE_CONFLICT', async () => {
B
Benjamin Pasero 已提交
741
		let event: FileOperationEvent;
742
		disposables.add(service.onAfterOperation(e => event = e));
B
Benjamin Pasero 已提交
743

744 745 746 747 748
		let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		const originalSize = source.size;
		assert.ok(originalSize > 0);

		let error;
B
Benjamin Pasero 已提交
749
		try {
B
Benjamin Pasero 已提交
750
			await service.move(source.resource, URI.file(join(testDir, 'binary.txt')));
B
Benjamin Pasero 已提交
751
		} catch (e) {
752
			error = e;
B
Benjamin Pasero 已提交
753
		}
754 755 756 757 758 759

		assert.equal(error.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT);
		assert.ok(!event!);

		source = await service.resolve(source.resource, { resolveMetadata: true });
		assert.equal(originalSize, source.size);
B
Benjamin Pasero 已提交
760 761
	});

B
Benjamin Pasero 已提交
762
	test('move - overwrite folder with file', async () => {
B
Benjamin Pasero 已提交
763 764 765
		let createEvent: FileOperationEvent;
		let moveEvent: FileOperationEvent;
		let deleteEvent: FileOperationEvent;
766
		disposables.add(service.onAfterOperation(e => {
B
Benjamin Pasero 已提交
767 768 769 770 771 772 773
			if (e.operation === FileOperation.CREATE) {
				createEvent = e;
			} else if (e.operation === FileOperation.DELETE) {
				deleteEvent = e;
			} else if (e.operation === FileOperation.MOVE) {
				moveEvent = e;
			}
774
		}));
B
Benjamin Pasero 已提交
775

B
Benjamin Pasero 已提交
776
		const parent = await service.resolve(URI.file(testDir));
B
Benjamin Pasero 已提交
777 778
		const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
		const f = await service.createFolder(folderResource);
779
		const source = URI.file(join(testDir, 'deep', 'conway.js'));
B
Benjamin Pasero 已提交
780

B
Benjamin Pasero 已提交
781
		const moved = await service.move(source, f.resource, true);
B
Benjamin Pasero 已提交
782 783 784 785 786 787

		assert.equal(existsSync(moved.resource.fsPath), true);
		assert.ok(statSync(moved.resource.fsPath).isFile);
		assert.ok(createEvent!);
		assert.ok(deleteEvent!);
		assert.ok(moveEvent!);
788
		assert.equal(moveEvent!.resource.fsPath, source.fsPath);
B
Benjamin Pasero 已提交
789 790 791
		assert.equal(moveEvent!.target!.resource.fsPath, moved.resource.fsPath);
		assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath);
	});
792

B
Benjamin Pasero 已提交
793 794
	test('copy', async () => {
		await doTestCopy();
795 796
	});

B
Benjamin Pasero 已提交
797
	test('copy - unbuffered (FileSystemProviderCapabilities.FileReadWrite)', async () => {
798 799
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

B
Benjamin Pasero 已提交
800
		await doTestCopy();
801 802
	});

B
Benjamin Pasero 已提交
803
	test('copy - unbuffered large (FileSystemProviderCapabilities.FileReadWrite)', async () => {
804 805
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

B
Benjamin Pasero 已提交
806
		await doTestCopy('lorem.txt');
807 808
	});

B
Benjamin Pasero 已提交
809
	test('copy - buffered (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
810 811
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

B
Benjamin Pasero 已提交
812
		await doTestCopy();
813 814
	});

B
Benjamin Pasero 已提交
815
	test('copy - buffered large (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {
816 817
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

B
Benjamin Pasero 已提交
818
		await doTestCopy('lorem.txt');
819 820 821 822 823 824 825 826 827
	});

	function setCapabilities(provider: TestDiskFileSystemProvider, capabilities: FileSystemProviderCapabilities): void {
		provider.capabilities = capabilities;
		if (isLinux) {
			provider.capabilities |= FileSystemProviderCapabilities.PathCaseSensitive;
		}
	}

B
Benjamin Pasero 已提交
828
	async function doTestCopy(sourceName: string = 'index.html') {
829
		let event: FileOperationEvent;
830
		disposables.add(service.onAfterOperation(e => event = e));
831

B
Benjamin Pasero 已提交
832
		const source = await service.resolve(URI.file(join(testDir, sourceName)));
833
		const target = URI.file(join(testDir, 'other.html'));
834

B
Benjamin Pasero 已提交
835
		const copied = await service.copy(source.resource, target);
836 837 838 839 840 841 842

		assert.equal(existsSync(copied.resource.fsPath), true);
		assert.equal(existsSync(source.resource.fsPath), true);
		assert.ok(event!);
		assert.equal(event!.resource.fsPath, source.resource.fsPath);
		assert.equal(event!.operation, FileOperation.COPY);
		assert.equal(event!.target!.resource.fsPath, copied.resource.fsPath);
843 844 845 846 847 848 849

		const sourceContents = readFileSync(source.resource.fsPath);
		const targetContents = readFileSync(target.fsPath);

		assert.equal(sourceContents.byteLength, targetContents.byteLength);
		assert.equal(sourceContents.toString(), targetContents.toString());
	}
850

B
Benjamin Pasero 已提交
851
	test('copy - overwrite folder with file', async () => {
852 853 854
		let createEvent: FileOperationEvent;
		let copyEvent: FileOperationEvent;
		let deleteEvent: FileOperationEvent;
855
		disposables.add(service.onAfterOperation(e => {
856 857 858 859 860 861 862
			if (e.operation === FileOperation.CREATE) {
				createEvent = e;
			} else if (e.operation === FileOperation.DELETE) {
				deleteEvent = e;
			} else if (e.operation === FileOperation.COPY) {
				copyEvent = e;
			}
863
		}));
864

B
Benjamin Pasero 已提交
865
		const parent = await service.resolve(URI.file(testDir));
866 867
		const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));
		const f = await service.createFolder(folderResource);
868
		const source = URI.file(join(testDir, 'deep', 'conway.js'));
869

B
Benjamin Pasero 已提交
870
		const copied = await service.copy(source, f.resource, true);
871 872 873 874 875 876

		assert.equal(existsSync(copied.resource.fsPath), true);
		assert.ok(statSync(copied.resource.fsPath).isFile);
		assert.ok(createEvent!);
		assert.ok(deleteEvent!);
		assert.ok(copyEvent!);
877
		assert.equal(copyEvent!.resource.fsPath, source.fsPath);
878 879 880 881
		assert.equal(copyEvent!.target!.resource.fsPath, copied.resource.fsPath);
		assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath);
	});

882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
	test('copy - MIX CASE same target - no overwrite', async () => {
		let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		const originalSize = source.size;
		assert.ok(originalSize > 0);

		const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));

		let error;
		let copied: IFileStatWithMetadata;
		try {
			copied = await service.copy(source.resource, target);
		} catch (e) {
			error = e;
		}

		if (isLinux) {
			assert.ok(!error);

			assert.equal(existsSync(copied!.resource.fsPath), true);
			assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));
			assert.equal(source.size, copied!.size);
		} else {
			assert.ok(error);

			source = await service.resolve(source.resource, { resolveMetadata: true });
			assert.equal(originalSize, source.size);
		}
	});

	test('copy - MIX CASE same target - overwrite', async () => {
		let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		const originalSize = source.size;
		assert.ok(originalSize > 0);

		const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));

		let error;
		let copied: IFileStatWithMetadata;
		try {
			copied = await service.copy(source.resource, target, true);
		} catch (e) {
			error = e;
		}

		if (isLinux) {
			assert.ok(!error);

			assert.equal(existsSync(copied!.resource.fsPath), true);
			assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));
			assert.equal(source.size, copied!.size);
		} else {
			assert.ok(error);

			source = await service.resolve(source.resource, { resolveMetadata: true });
			assert.equal(originalSize, source.size);
		}
	});

	test('copy - MIX CASE different taget - overwrite', async () => {
		const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		assert.ok(source.size > 0);

B
Benjamin Pasero 已提交
944
		const renamed = await service.move(source.resource, URI.file(join(dirname(source.resource.fsPath), 'CONWAY.js')));
945 946
		assert.equal(existsSync(renamed.resource.fsPath), true);
		assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js'));
947
		assert.equal(source.size, renamed.size);
948

949
		const source_1 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')), { resolveMetadata: true });
B
Benjamin Pasero 已提交
950
		const target = URI.file(join(testDir, basename(source_1.resource.path)));
951

B
Benjamin Pasero 已提交
952
		const res = await service.copy(source_1.resource, target, true);
953 954
		assert.equal(existsSync(res.resource.fsPath), true);
		assert.ok(readdirSync(testDir).some(f => f === 'conway.js'));
955
		assert.equal(source_1.size, res.size);
956 957
	});

958 959 960
	test('copy - same file', async () => {
		let event: FileOperationEvent;
		disposables.add(service.onAfterOperation(e => event = e));
961

962 963
		const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		assert.ok(source.size > 0);
964

965
		let copied = await service.copy(source.resource, URI.file(source.resource.fsPath));
966

967 968 969 970 971 972
		assert.equal(existsSync(copied.resource.fsPath), true);
		assert.equal(basename(copied.resource.fsPath), 'index.html');
		assert.ok(event!);
		assert.equal(event!.resource.fsPath, source.resource.fsPath);
		assert.equal(event!.operation, FileOperation.COPY);
		assert.equal(event!.target!.resource.fsPath, copied.resource.fsPath);
B
Benjamin Pasero 已提交
973

974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
		copied = await service.resolve(source.resource, { resolveMetadata: true });
		assert.equal(source.size, copied.size);
	});

	test('copy - same file #2', async () => {
		let event: FileOperationEvent;
		disposables.add(service.onAfterOperation(e => event = e));

		const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
		assert.ok(source.size > 0);

		const targetParent = URI.file(testDir);
		const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });

		let copied = await service.copy(source.resource, URI.file(target.fsPath));

		assert.equal(existsSync(copied.resource.fsPath), true);
		assert.equal(basename(copied.resource.fsPath), 'index.html');
		assert.ok(event!);
		assert.equal(event!.resource.fsPath, source.resource.fsPath);
		assert.equal(event!.operation, FileOperation.COPY);
		assert.equal(event!.target!.resource.fsPath, copied.resource.fsPath);

		copied = await service.resolve(source.resource, { resolveMetadata: true });
		assert.equal(source.size, copied.size);
999
	});
1000

1001 1002 1003 1004 1005 1006
	test('readFile - small file - buffered', () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		return testReadFile(URI.file(join(testDir, 'small.txt')));
	});

1007 1008 1009 1010 1011 1012
	test('readFile - small file - buffered / readonly', () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);

		return testReadFile(URI.file(join(testDir, 'small.txt')));
	});

1013 1014 1015 1016 1017 1018
	test('readFile - small file - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		return testReadFile(URI.file(join(testDir, 'small.txt')));
	});

1019 1020 1021 1022 1023 1024
	test('readFile - small file - unbuffered / readonly', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);

		return testReadFile(URI.file(join(testDir, 'small.txt')));
	});

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
	test('readFile - large file - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		return testReadFile(URI.file(join(testDir, 'lorem.txt')));
	});

	test('readFile - large file - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		return testReadFile(URI.file(join(testDir, 'lorem.txt')));
	});

	async function testReadFile(resource: URI): Promise<void> {
		const content = await service.readFile(resource);

		assert.equal(content.value.toString(), readFileSync(resource.fsPath));
	}

	test('readFile - Files are intermingled #38331 - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		let resource1 = URI.file(join(testDir, 'lorem.txt'));
		let resource2 = URI.file(join(testDir, 'some_utf16le.css'));

		// load in sequence and keep data
		const value1 = await service.readFile(resource1);
		const value2 = await service.readFile(resource2);

		// load in parallel in expect the same result
		const result = await Promise.all([
			service.readFile(resource1),
			service.readFile(resource2)
		]);

		assert.equal(result[0].value.toString(), value1.value.toString());
		assert.equal(result[1].value.toString(), value2.value.toString());
	});

	test('readFile - Files are intermingled #38331 - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		let resource1 = URI.file(join(testDir, 'lorem.txt'));
		let resource2 = URI.file(join(testDir, 'some_utf16le.css'));

		// load in sequence and keep data
		const value1 = await service.readFile(resource1);
		const value2 = await service.readFile(resource2);

		// load in parallel in expect the same result
		const result = await Promise.all([
			service.readFile(resource1),
			service.readFile(resource2)
		]);

		assert.equal(result[0].value.toString(), value1.value.toString());
		assert.equal(result[1].value.toString(), value2.value.toString());
	});

	test('readFile - from position (ASCII) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'small.txt'));

		const contents = await service.readFile(resource, { position: 6 });

		assert.equal(contents.value.toString(), 'File');
	});

	test('readFile - from position (with umlaut) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'small_umlaut.txt'));

		const contents = await service.readFile(resource, { position: Buffer.from('Small File with Ü').length });

		assert.equal(contents.value.toString(), 'mlaut');
	});

	test('readFile - from position (ASCII) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'small.txt'));

		const contents = await service.readFile(resource, { position: 6 });

		assert.equal(contents.value.toString(), 'File');
	});

	test('readFile - from position (with umlaut) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'small_umlaut.txt'));

		const contents = await service.readFile(resource, { position: Buffer.from('Small File with Ü').length });

		assert.equal(contents.value.toString(), 'mlaut');
	});

1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183

	test('readFile - 3 bytes (ASCII) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'small.txt'));

		const contents = await service.readFile(resource, { length: 3 });

		assert.equal(contents.value.toString(), 'Sma');
	});

	test('readFile - 3 bytes (ASCII) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'small.txt'));

		const contents = await service.readFile(resource, { length: 3 });

		assert.equal(contents.value.toString(), 'Sma');
	});

	test('readFile - 20000 bytes (large) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'lorem.txt'));

		const contents = await service.readFile(resource, { length: 20000 });

		assert.equal(contents.value.byteLength, 20000);
	});

	test('readFile - 20000 bytes (large) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'lorem.txt'));

		const contents = await service.readFile(resource, { length: 20000 });

		assert.equal(contents.value.byteLength, 20000);
	});

	test('readFile - 80000 bytes (large) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'lorem.txt'));

		const contents = await service.readFile(resource, { length: 80000 });

		assert.equal(contents.value.byteLength, 80000);
	});

	test('readFile - 80000 bytes (large) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'lorem.txt'));

		const contents = await service.readFile(resource, { length: 80000 });

		assert.equal(contents.value.byteLength, 80000);
	});

1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
	test('readFile - FILE_IS_DIRECTORY', async () => {
		const resource = URI.file(join(testDir, 'deep'));

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource);
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_IS_DIRECTORY);
	});

	test('readFile - FILE_NOT_FOUND', async () => {
		const resource = URI.file(join(testDir, '404.html'));

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource);
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_NOT_FOUND);
	});

	test('readFile - FILE_NOT_MODIFIED_SINCE - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'index.html'));

		const contents = await service.readFile(resource);
		fileProvider.totalBytesRead = 0;

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource, { etag: contents.etag });
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);
		assert.equal(fileProvider.totalBytesRead, 0);
	});

B
Benjamin Pasero 已提交
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
	test('readFile - FILE_NOT_MODIFIED_SINCE does not fire wrongly - https://github.com/Microsoft/vscode/issues/72909', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
		fileProvider.setInvalidStatSize(true);

		const resource = URI.file(join(testDir, 'index.html'));

		await service.readFile(resource);

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource, { etag: undefined });
		} catch (err) {
			error = err;
		}

		assert.ok(!error);
	});

1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
	test('readFile - FILE_NOT_MODIFIED_SINCE - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'index.html'));

		const contents = await service.readFile(resource);
		fileProvider.totalBytesRead = 0;

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource, { etag: contents.etag });
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);
		assert.equal(fileProvider.totalBytesRead, 0);
	});

	test('readFile - FILE_EXCEED_MEMORY_LIMIT - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'index.html'));

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource, { limits: { memory: 10 } });
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
	});

	test('readFile - FILE_EXCEED_MEMORY_LIMIT - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'index.html'));

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource, { limits: { memory: 10 } });
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
	});

	test('readFile - FILE_TOO_LARGE - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'index.html'));

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource, { limits: { size: 10 } });
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_TOO_LARGE);
	});

	test('readFile - FILE_TOO_LARGE - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

		const resource = URI.file(join(testDir, 'index.html'));

		let error: FileOperationError | undefined = undefined;
		try {
			await service.readFile(resource, { limits: { size: 10 } });
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_TOO_LARGE);
	});

1334
	test('createFile', async () => {
1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346
		assertCreateFile(contents => VSBuffer.fromString(contents));
	});

	test('createFile (readable)', async () => {
		assertCreateFile(contents => bufferToReadable(VSBuffer.fromString(contents)));
	});

	test('createFile (stream)', async () => {
		assertCreateFile(contents => bufferToStream(VSBuffer.fromString(contents)));
	});

	async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> {
1347
		let event: FileOperationEvent;
1348
		disposables.add(service.onAfterOperation(e => event = e));
1349 1350 1351

		const contents = 'Hello World';
		const resource = URI.file(join(testDir, 'test.txt'));
1352
		const fileStat = await service.createFile(resource, converter(contents));
1353 1354 1355 1356 1357 1358 1359 1360
		assert.equal(fileStat.name, 'test.txt');
		assert.equal(existsSync(fileStat.resource.fsPath), true);
		assert.equal(readFileSync(fileStat.resource.fsPath), contents);

		assert.ok(event!);
		assert.equal(event!.resource.fsPath, resource.fsPath);
		assert.equal(event!.operation, FileOperation.CREATE);
		assert.equal(event!.target!.resource.fsPath, resource.fsPath);
1361
	}
1362

1363
	test('createFile (does not overwrite by default)', async () => {
1364 1365 1366 1367 1368
		const contents = 'Hello World';
		const resource = URI.file(join(testDir, 'test.txt'));

		writeFileSync(resource.fsPath, ''); // create file

B
Benjamin Pasero 已提交
1369
		let error;
1370
		try {
1371
			await service.createFile(resource, VSBuffer.fromString(contents));
B
Benjamin Pasero 已提交
1372 1373
		} catch (err) {
			error = err;
1374
		}
B
Benjamin Pasero 已提交
1375 1376

		assert.ok(error);
1377 1378
	});

1379
	test('createFile (allows to overwrite existing)', async () => {
1380
		let event: FileOperationEvent;
1381
		disposables.add(service.onAfterOperation(e => event = e));
1382 1383 1384 1385 1386 1387

		const contents = 'Hello World';
		const resource = URI.file(join(testDir, 'test.txt'));

		writeFileSync(resource.fsPath, ''); // create file

1388
		const fileStat = await service.createFile(resource, VSBuffer.fromString(contents), { overwrite: true });
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
		assert.equal(fileStat.name, 'test.txt');
		assert.equal(existsSync(fileStat.resource.fsPath), true);
		assert.equal(readFileSync(fileStat.resource.fsPath), contents);

		assert.ok(event!);
		assert.equal(event!.resource.fsPath, resource.fsPath);
		assert.equal(event!.operation, FileOperation.CREATE);
		assert.equal(event!.target!.resource.fsPath, resource.fsPath);
	});

1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
	test('writeFile - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'small.txt'));

		const content = readFileSync(resource.fsPath);
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';
		await service.writeFile(resource, VSBuffer.fromString(newContent));

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

	test('writeFile (large file) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'lorem.txt'));

		const content = readFileSync(resource.fsPath);
		const newContent = content.toString() + content.toString();

		const fileStat = await service.writeFile(resource, VSBuffer.fromString(newContent));
		assert.equal(fileStat.name, 'lorem.txt');

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446
	test('writeFile - buffered - readonly throws', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);

		const resource = URI.file(join(testDir, 'small.txt'));

		const content = readFileSync(resource.fsPath);
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';

		let error: Error;
		try {
			await service.writeFile(resource, VSBuffer.fromString(newContent));
		} catch (err) {
			error = err;
		}

		assert.ok(error!);
	});

1447 1448 1449
	test('writeFile - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
		const resource = URI.file(join(testDir, 'small.txt'));

		const content = readFileSync(resource.fsPath);
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';
		await service.writeFile(resource, VSBuffer.fromString(newContent));

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

1461 1462 1463
	test('writeFile (large file) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474
		const resource = URI.file(join(testDir, 'lorem.txt'));

		const content = readFileSync(resource.fsPath);
		const newContent = content.toString() + content.toString();

		const fileStat = await service.writeFile(resource, VSBuffer.fromString(newContent));
		assert.equal(fileStat.name, 'lorem.txt');

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
	test('writeFile - unbuffered - readonly throws', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);

		const resource = URI.file(join(testDir, 'small.txt'));

		const content = readFileSync(resource.fsPath);
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';

		let error: Error;
		try {
			await service.writeFile(resource, VSBuffer.fromString(newContent));
		} catch (err) {
			error = err;
		}

		assert.ok(error!);
	});

B
Benjamin Pasero 已提交
1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
	test('writeFile (large file) - multiple parallel writes queue up', async () => {
		const resource = URI.file(join(testDir, 'lorem.txt'));

		const content = readFileSync(resource.fsPath);
		const newContent = content.toString() + content.toString();

		await Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {
			const fileStat = await service.writeFile(resource, VSBuffer.fromString(offset + newContent));
			assert.equal(fileStat.name, 'lorem.txt');
		}));

B
Benjamin Pasero 已提交
1506 1507
		const fileContent = readFileSync(resource.fsPath).toString();
		assert.ok(['0', '00', '000', '0000', '00000'].some(offset => fileContent === offset + newContent));
B
Benjamin Pasero 已提交
1508 1509
	});

1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
	test('writeFile (readable) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'small.txt'));

		const content = readFileSync(resource.fsPath);
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';
		await service.writeFile(resource, toLineByLineReadable(newContent));

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

	test('writeFile (large file - readable) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const resource = URI.file(join(testDir, 'lorem.txt'));

		const content = readFileSync(resource.fsPath);
		const newContent = content.toString() + content.toString();

		const fileStat = await service.writeFile(resource, toLineByLineReadable(newContent));
		assert.equal(fileStat.name, 'lorem.txt');

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

	test('writeFile (readable) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551
		const resource = URI.file(join(testDir, 'small.txt'));

		const content = readFileSync(resource.fsPath);
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';
		await service.writeFile(resource, toLineByLineReadable(newContent));

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

1552 1553 1554
	test('writeFile (large file - readable) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
		const resource = URI.file(join(testDir, 'lorem.txt'));

		const content = readFileSync(resource.fsPath);
		const newContent = content.toString() + content.toString();

		const fileStat = await service.writeFile(resource, toLineByLineReadable(newContent));
		assert.equal(fileStat.name, 'lorem.txt');

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

1566
	test('writeFile (stream) - buffered', async () => {
1567 1568
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

1569 1570
		const source = URI.file(join(testDir, 'small.txt'));
		const target = URI.file(join(testDir, 'small-copy.txt'));
1571

1572
		const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
1573
		assert.equal(fileStat.name, 'small-copy.txt');
1574

1575 1576
		assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
	});
1577

1578 1579 1580 1581 1582 1583
	test('writeFile (large file - stream) - buffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);

		const source = URI.file(join(testDir, 'lorem.txt'));
		const target = URI.file(join(testDir, 'lorem-copy.txt'));

1584
		const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
1585 1586 1587
		assert.equal(fileStat.name, 'lorem-copy.txt');

		assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
1588 1589 1590 1591 1592
	});

	test('writeFile (stream) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

1593 1594
		const source = URI.file(join(testDir, 'small.txt'));
		const target = URI.file(join(testDir, 'small-copy.txt'));
1595

1596
		const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
1597
		assert.equal(fileStat.name, 'small-copy.txt');
1598

1599
		assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
1600 1601 1602 1603 1604
	});

	test('writeFile (large file - stream) - unbuffered', async () => {
		setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);

1605 1606
		const source = URI.file(join(testDir, 'lorem.txt'));
		const target = URI.file(join(testDir, 'lorem-copy.txt'));
1607

1608
		const fileStat = await service.writeFile(target, toVSBufferReadableStream(createReadStream(source.fsPath)));
1609
		assert.equal(fileStat.name, 'lorem-copy.txt');
1610

1611
		assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString());
1612 1613
	});

1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650
	test('writeFile (file is created including parents)', async () => {
		const resource = URI.file(join(testDir, 'other', 'newfile.txt'));

		const content = 'File is created including parent';
		const fileStat = await service.writeFile(resource, VSBuffer.fromString(content));
		assert.equal(fileStat.name, 'newfile.txt');

		assert.equal(readFileSync(resource.fsPath), content);
	});

	test('writeFile (error when folder is encountered)', async () => {
		const resource = URI.file(testDir);

		let error: Error | undefined = undefined;
		try {
			await service.writeFile(resource, VSBuffer.fromString('File is created including parent'));
		} catch (err) {
			error = err;
		}

		assert.ok(error);
	});

	test('writeFile (no error when providing up to date etag)', async () => {
		const resource = URI.file(join(testDir, 'small.txt'));

		const stat = await service.resolve(resource);

		const content = readFileSync(resource.fsPath);
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';
		await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });

		assert.equal(readFileSync(resource.fsPath), newContent);
	});

1651
	test('writeFile - error when writing to file that has been updated meanwhile', async () => {
1652 1653
		const resource = URI.file(join(testDir, 'small.txt'));

B
Benjamin Pasero 已提交
1654
		const stat = await service.resolve(resource);
1655

1656
		const content = readFileSync(resource.fsPath).toString();
1657 1658 1659
		assert.equal(content, 'Small File');

		const newContent = 'Updates to the small file';
B
Benjamin Pasero 已提交
1660
		await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
1661 1662

		const newContentLeadingToError = newContent + newContent;
1663

B
Benjamin Pasero 已提交
1664 1665 1666
		const fakeMtime = 1000;
		const fakeSize = 1000;

1667 1668
		let error: FileOperationError | undefined = undefined;
		try {
B
Benjamin Pasero 已提交
1669
			await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToError), { etag: etag({ mtime: fakeMtime, size: fakeSize }), mtime: fakeMtime });
1670 1671 1672 1673 1674 1675 1676 1677 1678
		} catch (err) {
			error = err;
		}

		assert.ok(error);
		assert.ok(error instanceof FileOperationError);
		assert.equal(error!.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);
	});

1679 1680 1681
	test('writeFile - no error when writing to file where size is the same', async () => {
		const resource = URI.file(join(testDir, 'small.txt'));

B
Benjamin Pasero 已提交
1682
		const stat = await service.resolve(resource);
1683 1684 1685 1686 1687

		const content = readFileSync(resource.fsPath).toString();
		assert.equal(content, 'Small File');

		const newContent = content; // same content
B
Benjamin Pasero 已提交
1688
		await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
1689 1690 1691

		const newContentLeadingToNoError = newContent; // writing the same content should be OK

B
Benjamin Pasero 已提交
1692 1693 1694
		const fakeMtime = 1000;
		const actualSize = newContent.length;

1695 1696
		let error: FileOperationError | undefined = undefined;
		try {
B
Benjamin Pasero 已提交
1697
			await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToNoError), { etag: etag({ mtime: fakeMtime, size: actualSize }), mtime: fakeMtime });
1698 1699 1700 1701 1702 1703 1704
		} catch (err) {
			error = err;
		}

		assert.ok(!error);
	});

1705 1706 1707
	const runWatchTests = isLinux;

	(runWatchTests ? test : test.skip)('watch - file', done => {
1708 1709 1710
		const toWatch = URI.file(join(testDir, 'index-watch1.html'));
		writeFileSync(toWatch.fsPath, 'Init');

B
Benjamin Pasero 已提交
1711
		assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
1712

B
Benjamin Pasero 已提交
1713
		setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes'), 50);
1714 1715
	});

1716
	(runWatchTests && !isWindows /* symbolic links not reliable on windows */ ? test : test.skip)('watch - file symbolic link', async done => {
1717 1718 1719
		const toWatch = URI.file(join(testDir, 'lorem.txt-linked'));
		await symlink(join(testDir, 'lorem.txt'), toWatch.fsPath);

B
Benjamin Pasero 已提交
1720
		assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
1721 1722 1723 1724

		setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes'), 50);
	});

1725
	(runWatchTests ? test : test.skip)('watch - file - multiple writes', done => {
1726 1727 1728
		const toWatch = URI.file(join(testDir, 'index-watch1.html'));
		writeFileSync(toWatch.fsPath, 'Init');

B
Benjamin Pasero 已提交
1729
		assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
1730 1731 1732 1733 1734 1735

		setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes 1'), 0);
		setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes 2'), 10);
		setTimeout(() => writeFileSync(toWatch.fsPath, 'Changes 3'), 20);
	});

1736
	(runWatchTests ? test : test.skip)('watch - file - delete file', done => {
1737 1738 1739
		const toWatch = URI.file(join(testDir, 'index-watch1.html'));
		writeFileSync(toWatch.fsPath, 'Init');

B
Benjamin Pasero 已提交
1740
		assertWatch(toWatch, [[FileChangeType.DELETED, toWatch]], done);
1741 1742 1743 1744

		setTimeout(() => unlinkSync(toWatch.fsPath), 50);
	});

1745
	(runWatchTests ? test : test.skip)('watch - file - rename file', done => {
B
Benjamin Pasero 已提交
1746 1747 1748 1749 1750 1751 1752 1753 1754
		const toWatch = URI.file(join(testDir, 'index-watch1.html'));
		const toWatchRenamed = URI.file(join(testDir, 'index-watch1-renamed.html'));
		writeFileSync(toWatch.fsPath, 'Init');

		assertWatch(toWatch, [[FileChangeType.DELETED, toWatch]], done);

		setTimeout(() => renameSync(toWatch.fsPath, toWatchRenamed.fsPath), 50);
	});

1755
	(runWatchTests ? test : test.skip)('watch - file - rename file (different case)', done => {
B
Benjamin Pasero 已提交
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768
		const toWatch = URI.file(join(testDir, 'index-watch1.html'));
		const toWatchRenamed = URI.file(join(testDir, 'INDEX-watch1.html'));
		writeFileSync(toWatch.fsPath, 'Init');

		if (isLinux) {
			assertWatch(toWatch, [[FileChangeType.DELETED, toWatch]], done);
		} else {
			assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done); // case insensitive file system treat this as change
		}

		setTimeout(() => renameSync(toWatch.fsPath, toWatchRenamed.fsPath), 50);
	});

1769
	(runWatchTests ? test : test.skip)('watch - file (atomic save)', function (done) {
1770 1771 1772
		const toWatch = URI.file(join(testDir, 'index-watch2.html'));
		writeFileSync(toWatch.fsPath, 'Init');

B
Benjamin Pasero 已提交
1773
		assertWatch(toWatch, [[FileChangeType.UPDATED, toWatch]], done);
1774 1775 1776 1777 1778 1779 1780 1781

		setTimeout(() => {
			// Simulate atomic save by deleting the file, creating it under different name
			// and then replacing the previously deleted file with those contents
			const renamed = `${toWatch.fsPath}.bak`;
			unlinkSync(toWatch.fsPath);
			writeFileSync(renamed, 'Changes');
			renameSync(renamed, toWatch.fsPath);
1782
		}, 50);
1783 1784
	});

1785
	(runWatchTests ? test : test.skip)('watch - folder (non recursive) - change file', done => {
1786 1787 1788 1789 1790 1791
		const watchDir = URI.file(join(testDir, 'watch3'));
		mkdirSync(watchDir.fsPath);

		const file = URI.file(join(watchDir.fsPath, 'index.html'));
		writeFileSync(file.fsPath, 'Init');

B
Benjamin Pasero 已提交
1792
		assertWatch(watchDir, [[FileChangeType.UPDATED, file]], done);
1793

B
Benjamin Pasero 已提交
1794
		setTimeout(() => writeFileSync(file.fsPath, 'Changes'), 50);
1795 1796
	});

1797
	(runWatchTests ? test : test.skip)('watch - folder (non recursive) - add file', done => {
1798 1799 1800 1801 1802
		const watchDir = URI.file(join(testDir, 'watch4'));
		mkdirSync(watchDir.fsPath);

		const file = URI.file(join(watchDir.fsPath, 'index.html'));

B
Benjamin Pasero 已提交
1803
		assertWatch(watchDir, [[FileChangeType.ADDED, file]], done);
1804

B
Benjamin Pasero 已提交
1805
		setTimeout(() => writeFileSync(file.fsPath, 'Changes'), 50);
1806 1807
	});

1808
	(runWatchTests ? test : test.skip)('watch - folder (non recursive) - delete file', done => {
1809 1810 1811 1812 1813 1814
		const watchDir = URI.file(join(testDir, 'watch5'));
		mkdirSync(watchDir.fsPath);

		const file = URI.file(join(watchDir.fsPath, 'index.html'));
		writeFileSync(file.fsPath, 'Init');

B
Benjamin Pasero 已提交
1815
		assertWatch(watchDir, [[FileChangeType.DELETED, file]], done);
1816

B
Benjamin Pasero 已提交
1817
		setTimeout(() => unlinkSync(file.fsPath), 50);
1818 1819
	});

1820
	(runWatchTests ? test : test.skip)('watch - folder (non recursive) - add folder', done => {
1821 1822
		const watchDir = URI.file(join(testDir, 'watch6'));
		mkdirSync(watchDir.fsPath);
1823

1824
		const folder = URI.file(join(watchDir.fsPath, 'folder'));
1825

1826
		assertWatch(watchDir, [[FileChangeType.ADDED, folder]], done);
1827

1828 1829
		setTimeout(() => mkdirSync(folder.fsPath), 50);
	});
1830

1831
	(runWatchTests ? test : test.skip)('watch - folder (non recursive) - delete folder', done => {
1832 1833 1834 1835 1836 1837
		const watchDir = URI.file(join(testDir, 'watch7'));
		mkdirSync(watchDir.fsPath);

		const folder = URI.file(join(watchDir.fsPath, 'folder'));
		mkdirSync(folder.fsPath);

B
Benjamin Pasero 已提交
1838
		assertWatch(watchDir, [[FileChangeType.DELETED, folder]], done);
B
Benjamin Pasero 已提交
1839 1840 1841 1842

		setTimeout(() => rimrafSync(folder.fsPath), 50);
	});

1843
	(runWatchTests && !isWindows /* symbolic links not reliable on windows */ ? test : test.skip)('watch - folder (non recursive) - symbolic link - change file', async done => {
1844 1845 1846 1847 1848 1849
		const watchDir = URI.file(join(testDir, 'deep-link'));
		await symlink(join(testDir, 'deep'), watchDir.fsPath);

		const file = URI.file(join(watchDir.fsPath, 'index.html'));
		writeFileSync(file.fsPath, 'Init');

B
Benjamin Pasero 已提交
1850
		assertWatch(watchDir, [[FileChangeType.UPDATED, file]], done);
1851 1852 1853 1854

		setTimeout(() => writeFileSync(file.fsPath, 'Changes'), 50);
	});

1855
	(runWatchTests ? test : test.skip)('watch - folder (non recursive) - rename file', done => {
B
Benjamin Pasero 已提交
1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868
		const watchDir = URI.file(join(testDir, 'watch8'));
		mkdirSync(watchDir.fsPath);

		const file = URI.file(join(watchDir.fsPath, 'index.html'));
		writeFileSync(file.fsPath, 'Init');

		const fileRenamed = URI.file(join(watchDir.fsPath, 'index-renamed.html'));

		assertWatch(watchDir, [[FileChangeType.DELETED, file], [FileChangeType.ADDED, fileRenamed]], done);

		setTimeout(() => renameSync(file.fsPath, fileRenamed.fsPath), 50);
	});

B
Benjamin Pasero 已提交
1869
	(runWatchTests && isLinux /* this test requires a case sensitive file system */ ? test : test.skip)('watch - folder (non recursive) - rename file (different case)', done => {
B
Benjamin Pasero 已提交
1870 1871 1872 1873 1874 1875 1876 1877
		const watchDir = URI.file(join(testDir, 'watch8'));
		mkdirSync(watchDir.fsPath);

		const file = URI.file(join(watchDir.fsPath, 'index.html'));
		writeFileSync(file.fsPath, 'Init');

		const fileRenamed = URI.file(join(watchDir.fsPath, 'INDEX.html'));

1878
		assertWatch(watchDir, [[FileChangeType.DELETED, file], [FileChangeType.ADDED, fileRenamed]], done);
B
Benjamin Pasero 已提交
1879 1880 1881 1882 1883

		setTimeout(() => renameSync(file.fsPath, fileRenamed.fsPath), 50);
	});

	function assertWatch(toWatch: URI, expected: [FileChangeType, URI][], done: MochaDone): void {
B
Benjamin Pasero 已提交
1884 1885
		const watcherDisposable = service.watch(toWatch);

B
Benjamin Pasero 已提交
1886 1887 1888 1889 1890 1891 1892 1893
		function toString(type: FileChangeType): string {
			switch (type) {
				case FileChangeType.ADDED: return 'added';
				case FileChangeType.DELETED: return 'deleted';
				case FileChangeType.UPDATED: return 'updated';
			}
		}

1894 1895 1896 1897
		function printEvents(event: FileChangesEvent): string {
			return event.changes.map(change => `Change: type ${toString(change.type)} path ${change.resource.toString()}`).join('\n');
		}

1898 1899 1900 1901 1902
		const listenerDisposable = service.onFileChanges(event => {
			watcherDisposable.dispose();
			listenerDisposable.dispose();

			try {
1903
				assert.equal(event.changes.length, expected.length, `Expected ${expected.length} events, but got ${event.changes.length}. Details (${printEvents(event)})`);
B
Benjamin Pasero 已提交
1904 1905

				if (expected.length === 1) {
1906
					assert.equal(event.changes[0].type, expected[0][0], `Expected ${toString(expected[0][0])} but got ${toString(event.changes[0].type)}. Details (${printEvents(event)})`);
B
Benjamin Pasero 已提交
1907 1908 1909
					assert.equal(event.changes[0].resource.fsPath, expected[0][1].fsPath);
				} else {
					for (const expect of expected) {
1910
						assert.equal(hasChange(event.changes, expect[0], expect[1]), true, `Unable to find ${toString(expect[0])} for ${expect[1].fsPath}. Details (${printEvents(event)})`);
B
Benjamin Pasero 已提交
1911 1912
					}
				}
1913 1914 1915 1916 1917 1918

				done();
			} catch (error) {
				done(error);
			}
		});
B
Benjamin Pasero 已提交
1919
	}
B
Benjamin Pasero 已提交
1920 1921 1922 1923

	function hasChange(changes: IFileChange[], type: FileChangeType, resource: URI): boolean {
		return changes.some(change => change.type === type && isEqual(change.resource, resource));
	}
1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014

	test('read - mixed positions', async () => {
		const resource = URI.file(join(testDir, 'lorem.txt'));

		// read multiple times from position 0
		let buffer = VSBuffer.alloc(1024);
		let fd = await fileProvider.open(resource, { create: false });
		for (let i = 0; i < 3; i++) {
			await fileProvider.read(fd, 0, buffer.buffer, 0, 26);
			assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
		}
		await fileProvider.close(fd);

		// read multiple times at various locations
		buffer = VSBuffer.alloc(1024);
		fd = await fileProvider.open(resource, { create: false });

		let posInFile = 0;

		await fileProvider.read(fd, posInFile, buffer.buffer, 0, 26);
		assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
		posInFile += 26;

		await fileProvider.read(fd, posInFile, buffer.buffer, 0, 1);
		assert.equal(buffer.slice(0, 1).toString(), ',');
		posInFile += 1;

		await fileProvider.read(fd, posInFile, buffer.buffer, 0, 12);
		assert.equal(buffer.slice(0, 12).toString(), ' consectetur');
		posInFile += 12;

		await fileProvider.read(fd, 98 /* no longer in sequence of posInFile */, buffer.buffer, 0, 9);
		assert.equal(buffer.slice(0, 9).toString(), 'fermentum');

		await fileProvider.read(fd, 27, buffer.buffer, 0, 12);
		assert.equal(buffer.slice(0, 12).toString(), ' consectetur');

		await fileProvider.read(fd, 26, buffer.buffer, 0, 1);
		assert.equal(buffer.slice(0, 1).toString(), ',');

		await fileProvider.read(fd, 0, buffer.buffer, 0, 26);
		assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');

		await fileProvider.read(fd, posInFile /* back in sequence */, buffer.buffer, 0, 11);
		assert.equal(buffer.slice(0, 11).toString(), ' adipiscing');

		await fileProvider.close(fd);
	});

	test('write - mixed positions', async () => {
		const resource = URI.file(join(testDir, 'lorem.txt'));

		const buffer = VSBuffer.alloc(1024);
		const fdWrite = await fileProvider.open(resource, { create: true });
		const fdRead = await fileProvider.open(resource, { create: false });

		let posInFileWrite = 0;
		let posInFileRead = 0;

		const initialContents = VSBuffer.fromString('Lorem ipsum dolor sit amet');
		await fileProvider.write(fdWrite, posInFileWrite, initialContents.buffer, 0, initialContents.byteLength);
		posInFileWrite += initialContents.byteLength;

		await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, 26);
		assert.equal(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');
		posInFileRead += 26;

		const contents = VSBuffer.fromString('Hello World');

		await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);
		posInFileWrite += contents.byteLength;

		await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, contents.byteLength);
		assert.equal(buffer.slice(0, contents.byteLength).toString(), 'Hello World');
		posInFileRead += contents.byteLength;

		await fileProvider.write(fdWrite, 6, contents.buffer, 0, contents.byteLength);

		await fileProvider.read(fdRead, 0, buffer.buffer, 0, 11);
		assert.equal(buffer.slice(0, 11).toString(), 'Lorem Hello');

		await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);
		posInFileWrite += contents.byteLength;

		await fileProvider.read(fdRead, posInFileWrite - contents.byteLength, buffer.buffer, 0, contents.byteLength);
		assert.equal(buffer.slice(0, contents.byteLength).toString(), 'Hello World');

		await fileProvider.close(fdWrite);
		await fileProvider.close(fdRead);
	});
});