Path: blob/main/src/vs/platform/files/test/node/diskFileService.integrationTest.ts
5228 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { createReadStream, existsSync, readdirSync, readFileSync, statSync, writeFileSync, promises } from 'fs';7import { tmpdir } from 'os';8import { timeout } from '../../../../base/common/async.js';9import { bufferToReadable, bufferToStream, streamToBuffer, streamToBufferReadableStream, VSBuffer, VSBufferReadable, VSBufferReadableStream } from '../../../../base/common/buffer.js';10import { DisposableStore } from '../../../../base/common/lifecycle.js';11import { FileAccess, Schemas } from '../../../../base/common/network.js';12import { basename, dirname, join, posix } from '../../../../base/common/path.js';13import { isLinux, isWindows } from '../../../../base/common/platform.js';14import { joinPath } from '../../../../base/common/resources.js';15import { URI } from '../../../../base/common/uri.js';16import { Promises } from '../../../../base/node/pfs.js';17import { flakySuite, getRandomTestPath } from '../../../../base/test/node/testUtils.js';18import { etag, IFileAtomicReadOptions, FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FilePermission, FileSystemProviderCapabilities, hasFileAtomicReadCapability, hasOpenReadWriteCloseCapability, IFileStat, IFileStatWithMetadata, IReadFileOptions, IStat, NotModifiedSinceFileOperationError, TooLargeFileOperationError, IFileAtomicOptions } from '../../common/files.js';19import { FileService } from '../../common/fileService.js';20import { DiskFileSystemProvider } from '../../node/diskFileSystemProvider.js';21import { NullLogService } from '../../../log/common/log.js';2223function getByName(root: IFileStat, name: string): IFileStat | undefined {24if (root.children === undefined) {25return undefined;26}2728return root.children.find(child => child.name === name);29}3031function toLineByLineReadable(content: string): VSBufferReadable {32let chunks = content.split('\n');33chunks = chunks.map((chunk, index) => {34if (index === 0) {35return chunk;36}3738return '\n' + chunk;39});4041return {42read(): VSBuffer | null {43const chunk = chunks.shift();44if (typeof chunk === 'string') {45return VSBuffer.fromString(chunk);46}4748return null;49}50};51}5253export class TestDiskFileSystemProvider extends DiskFileSystemProvider {5455totalBytesRead: number = 0;5657private invalidStatSize: boolean = false;58private smallStatSize: boolean = false;59private readonly: boolean = false;6061private _testCapabilities!: FileSystemProviderCapabilities;62override get capabilities(): FileSystemProviderCapabilities {63if (!this._testCapabilities) {64this._testCapabilities =65FileSystemProviderCapabilities.FileReadWrite |66FileSystemProviderCapabilities.FileOpenReadWriteClose |67FileSystemProviderCapabilities.FileReadStream |68FileSystemProviderCapabilities.Trash |69FileSystemProviderCapabilities.FileFolderCopy |70FileSystemProviderCapabilities.FileWriteUnlock |71FileSystemProviderCapabilities.FileAtomicRead |72FileSystemProviderCapabilities.FileAtomicWrite |73FileSystemProviderCapabilities.FileAtomicDelete |74FileSystemProviderCapabilities.FileClone |75FileSystemProviderCapabilities.FileAppend |76FileSystemProviderCapabilities.FileRealpath;7778if (isLinux) {79this._testCapabilities |= FileSystemProviderCapabilities.PathCaseSensitive;80}81}8283return this._testCapabilities;84}8586override set capabilities(capabilities: FileSystemProviderCapabilities) {87this._testCapabilities = capabilities;88}8990setInvalidStatSize(enabled: boolean): void {91this.invalidStatSize = enabled;92}9394setSmallStatSize(enabled: boolean): void {95this.smallStatSize = enabled;96}9798setReadonly(readonly: boolean): void {99this.readonly = readonly;100}101102override async stat(resource: URI): Promise<IStat> {103const res = await super.stat(resource);104105if (this.invalidStatSize) {106// eslint-disable-next-line local/code-no-any-casts107(res as any).size = String(res.size) as any; // for https://github.com/microsoft/vscode/issues/72909108} else if (this.smallStatSize) {109// eslint-disable-next-line local/code-no-any-casts110(res as any).size = 1;111} else if (this.readonly) {112// eslint-disable-next-line local/code-no-any-casts113(res as any).permissions = FilePermission.Readonly;114}115116return res;117}118119override async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {120const bytesRead = await super.read(fd, pos, data, offset, length);121122this.totalBytesRead += bytesRead;123124return bytesRead;125}126127override async readFile(resource: URI, options?: IFileAtomicReadOptions): Promise<Uint8Array> {128const res = await super.readFile(resource, options);129130this.totalBytesRead += res.byteLength;131132return res;133}134}135136DiskFileSystemProvider.configureFlushOnWrite(false); // speed up all unit tests by disabling flush on write137138flakySuite('Disk File Service', function () {139140const testSchema = 'test';141142let service: FileService;143let fileProvider: TestDiskFileSystemProvider;144let testProvider: TestDiskFileSystemProvider;145146let testDir: string;147148const disposables = new DisposableStore();149150setup(async () => {151const logService = new NullLogService();152153service = disposables.add(new FileService(logService));154155fileProvider = disposables.add(new TestDiskFileSystemProvider(logService));156disposables.add(service.registerProvider(Schemas.file, fileProvider));157158testProvider = disposables.add(new TestDiskFileSystemProvider(logService));159disposables.add(service.registerProvider(testSchema, testProvider));160161testDir = getRandomTestPath(tmpdir(), 'vsctests', 'diskfileservice');162163const sourceDir = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/service').fsPath;164165await Promises.copy(sourceDir, testDir, { preserveSymlinks: false });166});167168teardown(() => {169disposables.clear();170171return Promises.rm(testDir);172});173174test('createFolder', async () => {175let event: FileOperationEvent | undefined;176disposables.add(service.onDidRunOperation(e => event = e));177178const parent = await service.resolve(URI.file(testDir));179180const newFolderResource = URI.file(join(parent.resource.fsPath, 'newFolder'));181182const newFolder = await service.createFolder(newFolderResource);183184assert.strictEqual(newFolder.name, 'newFolder');185assert.strictEqual(existsSync(newFolder.resource.fsPath), true);186187assert.ok(event);188assert.strictEqual(event.resource.fsPath, newFolderResource.fsPath);189assert.strictEqual(event.operation, FileOperation.CREATE);190assert.strictEqual(event.target!.resource.fsPath, newFolderResource.fsPath);191assert.strictEqual(event.target!.isDirectory, true);192});193194test('createFolder: creating multiple folders at once', async () => {195let event: FileOperationEvent;196disposables.add(service.onDidRunOperation(e => event = e));197198const multiFolderPaths = ['a', 'couple', 'of', 'folders'];199const parent = await service.resolve(URI.file(testDir));200201const newFolderResource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths));202203const newFolder = await service.createFolder(newFolderResource);204205const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1];206assert.strictEqual(newFolder.name, lastFolderName);207assert.strictEqual(existsSync(newFolder.resource.fsPath), true);208209assert.ok(event!);210assert.strictEqual(event!.resource.fsPath, newFolderResource.fsPath);211assert.strictEqual(event!.operation, FileOperation.CREATE);212assert.strictEqual(event!.target!.resource.fsPath, newFolderResource.fsPath);213assert.strictEqual(event!.target!.isDirectory, true);214});215216test('exists', async () => {217let exists = await service.exists(URI.file(testDir));218assert.strictEqual(exists, true);219220exists = await service.exists(URI.file(testDir + 'something'));221assert.strictEqual(exists, false);222});223224test('resolve - file', async () => {225const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/index.html');226const resolved = await service.resolve(resource);227228assert.strictEqual(resolved.name, 'index.html');229assert.strictEqual(resolved.isFile, true);230assert.strictEqual(resolved.isDirectory, false);231assert.strictEqual(resolved.readonly, false);232assert.strictEqual(resolved.isSymbolicLink, false);233assert.strictEqual(resolved.resource.toString(), resource.toString());234assert.strictEqual(resolved.children, undefined);235assert.ok(resolved.mtime! > 0);236assert.ok(resolved.ctime! > 0);237assert.ok(resolved.size! > 0);238});239240test('resolve - directory', async () => {241const testsElements = ['examples', 'other', 'index.html', 'site.css'];242243const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver');244const result = await service.resolve(resource);245246assert.ok(result);247assert.strictEqual(result.resource.toString(), resource.toString());248assert.strictEqual(result.name, 'resolver');249assert.ok(result.children);250assert.ok(result.children.length > 0);251assert.ok(result.isDirectory);252assert.strictEqual(result.readonly, false);253assert.ok(result.mtime! > 0);254assert.ok(result.ctime! > 0);255assert.strictEqual(result.children.length, testsElements.length);256257assert.ok(result.children.every(entry => {258return testsElements.some(name => {259return basename(entry.resource.fsPath) === name;260});261}));262263result.children.forEach(value => {264assert.ok(basename(value.resource.fsPath));265if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {266assert.ok(value.isDirectory);267assert.strictEqual(value.mtime, undefined);268assert.strictEqual(value.ctime, undefined);269} else if (basename(value.resource.fsPath) === 'index.html') {270assert.ok(!value.isDirectory);271assert.ok(!value.children);272assert.strictEqual(value.mtime, undefined);273assert.strictEqual(value.ctime, undefined);274} else if (basename(value.resource.fsPath) === 'site.css') {275assert.ok(!value.isDirectory);276assert.ok(!value.children);277assert.strictEqual(value.mtime, undefined);278assert.strictEqual(value.ctime, undefined);279} else {280assert.fail('Unexpected value ' + basename(value.resource.fsPath));281}282});283});284285test('resolve - directory - with metadata', async () => {286const testsElements = ['examples', 'other', 'index.html', 'site.css'];287288const result = await service.resolve(FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver'), { resolveMetadata: true });289290assert.ok(result);291assert.strictEqual(result.name, 'resolver');292assert.ok(result.children);293assert.ok(result.children.length > 0);294assert.ok(result.isDirectory);295assert.ok(result.mtime > 0);296assert.ok(result.ctime > 0);297assert.strictEqual(result.children.length, testsElements.length);298299assert.ok(result.children.every(entry => {300return testsElements.some(name => {301return basename(entry.resource.fsPath) === name;302});303}));304305assert.ok(result.children.every(entry => entry.etag.length > 0));306307result.children.forEach(value => {308assert.ok(basename(value.resource.fsPath));309if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {310assert.ok(value.isDirectory);311assert.ok(value.mtime > 0);312assert.ok(value.ctime > 0);313} else if (basename(value.resource.fsPath) === 'index.html') {314assert.ok(!value.isDirectory);315assert.ok(!value.children);316assert.ok(value.mtime > 0);317assert.ok(value.ctime > 0);318} else if (basename(value.resource.fsPath) === 'site.css') {319assert.ok(!value.isDirectory);320assert.ok(!value.children);321assert.ok(value.mtime > 0);322assert.ok(value.ctime > 0);323} else {324assert.fail('Unexpected value ' + basename(value.resource.fsPath));325}326});327});328329test('resolve - directory with resolveTo', async () => {330const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });331assert.strictEqual(resolved.children!.length, 8);332333const deep = (getByName(resolved, 'deep')!);334assert.strictEqual(deep.children!.length, 4);335});336337test('resolve - directory - resolveTo single directory', async () => {338const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver').fsPath;339const result = await service.resolve(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] });340341assert.ok(result);342assert.ok(result.children);343assert.ok(result.children.length > 0);344assert.ok(result.isDirectory);345346const children = result.children;347assert.strictEqual(children.length, 4);348349const other = getByName(result, 'other');350assert.ok(other);351assert.ok(other.children!.length > 0);352353const deep = getByName(other, 'deep');354assert.ok(deep);355assert.ok(deep.children!.length > 0);356assert.strictEqual(deep.children!.length, 4);357});358359test('resolve directory - resolveTo multiple directories', () => {360return testResolveDirectoryWithTarget(false);361});362363test('resolve directory - resolveTo with a URI that has query parameter (https://github.com/microsoft/vscode/issues/128151)', () => {364return testResolveDirectoryWithTarget(true);365});366367async function testResolveDirectoryWithTarget(withQueryParam: boolean): Promise<void> {368const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver').fsPath;369const result = await service.resolve(URI.file(resolverFixturesPath).with({ query: withQueryParam ? 'test' : undefined }), {370resolveTo: [371URI.file(join(resolverFixturesPath, 'other/deep')).with({ query: withQueryParam ? 'test' : undefined }),372URI.file(join(resolverFixturesPath, 'examples')).with({ query: withQueryParam ? 'test' : undefined })373]374});375376assert.ok(result);377assert.ok(result.children);378assert.ok(result.children.length > 0);379assert.ok(result.isDirectory);380381const children = result.children;382assert.strictEqual(children.length, 4);383384const other = getByName(result, 'other');385assert.ok(other);386assert.ok(other.children!.length > 0);387388const deep = getByName(other, 'deep');389assert.ok(deep);390assert.ok(deep.children!.length > 0);391assert.strictEqual(deep.children!.length, 4);392393const examples = getByName(result, 'examples');394assert.ok(examples);395assert.ok(examples.children!.length > 0);396assert.strictEqual(examples.children!.length, 4);397}398399test('resolve directory - resolveSingleChildFolders', async () => {400const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/other').fsPath;401const result = await service.resolve(URI.file(resolverFixturesPath), { resolveSingleChildDescendants: true });402403assert.ok(result);404assert.ok(result.children);405assert.ok(result.children.length > 0);406assert.ok(result.isDirectory);407408const children = result.children;409assert.strictEqual(children.length, 1);410411const deep = getByName(result, 'deep');412assert.ok(deep);413assert.ok(deep.children!.length > 0);414assert.strictEqual(deep.children!.length, 4);415});416417test('resolves', async () => {418const res = await service.resolveAll([419{ resource: URI.file(testDir), options: { resolveTo: [URI.file(join(testDir, 'deep'))] } },420{ resource: URI.file(join(testDir, 'deep')) }421]);422423const r1 = (res[0].stat!);424assert.strictEqual(r1.children!.length, 8);425426const deep = (getByName(r1, 'deep')!);427assert.strictEqual(deep.children!.length, 4);428429const r2 = (res[1].stat!);430assert.strictEqual(r2.children!.length, 4);431assert.strictEqual(r2.name, 'deep');432});433434test('resolve / realpath - folder symbolic link', async () => {435const link = URI.file(join(testDir, 'deep-link'));436await promises.symlink(join(testDir, 'deep'), link.fsPath, 'junction');437438const resolved = await service.resolve(link);439assert.strictEqual(resolved.children!.length, 4);440assert.strictEqual(resolved.isDirectory, true);441assert.strictEqual(resolved.isSymbolicLink, true);442443const realpath = await service.realpath(link);444assert.ok(realpath);445assert.strictEqual(basename(realpath.fsPath), 'deep');446});447448(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('resolve - file symbolic link', async () => {449const link = URI.file(join(testDir, 'lorem.txt-linked'));450await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath);451452const resolved = await service.resolve(link);453assert.strictEqual(resolved.isDirectory, false);454assert.strictEqual(resolved.isSymbolicLink, true);455});456457test('resolve - symbolic link pointing to nonexistent file does not break', async () => {458await promises.symlink(join(testDir, 'foo'), join(testDir, 'bar'), 'junction');459460const resolved = await service.resolve(URI.file(testDir));461assert.strictEqual(resolved.isDirectory, true);462assert.strictEqual(resolved.children!.length, 9);463464const resolvedLink = resolved.children?.find(child => child.name === 'bar' && child.isSymbolicLink);465assert.ok(resolvedLink);466467assert.ok(!resolvedLink?.isDirectory);468assert.ok(!resolvedLink?.isFile);469});470471test('stat - file', async () => {472const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/index.html');473const resolved = await service.stat(resource);474475assert.strictEqual(resolved.name, 'index.html');476assert.strictEqual(resolved.isFile, true);477assert.strictEqual(resolved.isDirectory, false);478assert.strictEqual(resolved.readonly, false);479assert.strictEqual(resolved.isSymbolicLink, false);480assert.strictEqual(resolved.resource.toString(), resource.toString());481assert.ok(resolved.mtime > 0);482assert.ok(resolved.ctime > 0);483assert.ok(resolved.size > 0);484});485486test('stat - directory', async () => {487const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver');488const result = await service.stat(resource);489490assert.ok(result);491assert.strictEqual(result.resource.toString(), resource.toString());492assert.strictEqual(result.name, 'resolver');493assert.ok(result.isDirectory);494assert.strictEqual(result.readonly, false);495assert.ok(result.mtime > 0);496assert.ok(result.ctime > 0);497});498499// The executable bit does not exist on Windows so use a condition not skip500if (!isWindows) {501test('stat - executable', async () => {502const nonExecutable = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/executable/non_executable');503let resolved = await service.stat(nonExecutable);504assert.strictEqual(resolved.isFile, true);505assert.strictEqual(resolved.executable, false);506507const executable = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/executable/executable');508resolved = await service.stat(executable);509assert.strictEqual(resolved.isFile, true);510assert.strictEqual(resolved.executable, true);511});512}513514test('deleteFile (non recursive)', async () => {515return testDeleteFile(false, false);516});517518test('deleteFile (recursive)', async () => {519return testDeleteFile(false, true);520});521522(isLinux /* trash is unreliable on Linux */ ? test.skip : test)('deleteFile (useTrash)', async () => {523return testDeleteFile(true, false);524});525526async function testDeleteFile(useTrash: boolean, recursive: boolean): Promise<void> {527let event: FileOperationEvent;528disposables.add(service.onDidRunOperation(e => event = e));529530const resource = URI.file(join(testDir, 'deep', 'conway.js'));531const source = await service.resolve(resource);532533assert.strictEqual(await service.canDelete(source.resource, { useTrash, recursive }), true);534await service.del(source.resource, { useTrash, recursive });535536assert.strictEqual(existsSync(source.resource.fsPath), false);537538assert.ok(event!);539assert.strictEqual(event!.resource.fsPath, resource.fsPath);540assert.strictEqual(event!.operation, FileOperation.DELETE);541542let error: Error | undefined = undefined;543try {544await service.del(source.resource, { useTrash, recursive });545} catch (e) {546error = e;547}548549assert.ok(error);550assert.strictEqual((<FileOperationError>error).fileOperationResult, FileOperationResult.FILE_NOT_FOUND);551}552553(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (exists)', async () => {554const target = URI.file(join(testDir, 'lorem.txt'));555const link = URI.file(join(testDir, 'lorem.txt-linked'));556await promises.symlink(target.fsPath, link.fsPath);557558const source = await service.resolve(link);559560let event: FileOperationEvent;561disposables.add(service.onDidRunOperation(e => event = e));562563assert.strictEqual(await service.canDelete(source.resource), true);564await service.del(source.resource);565566assert.strictEqual(existsSync(source.resource.fsPath), false);567568assert.ok(event!);569assert.strictEqual(event!.resource.fsPath, link.fsPath);570assert.strictEqual(event!.operation, FileOperation.DELETE);571572assert.strictEqual(existsSync(target.fsPath), true); // target the link pointed to is never deleted573});574575(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (pointing to nonexistent file)', async () => {576const target = URI.file(join(testDir, 'foo'));577const link = URI.file(join(testDir, 'bar'));578await promises.symlink(target.fsPath, link.fsPath);579580let event: FileOperationEvent;581disposables.add(service.onDidRunOperation(e => event = e));582583assert.strictEqual(await service.canDelete(link), true);584await service.del(link);585586assert.strictEqual(existsSync(link.fsPath), false);587588assert.ok(event!);589assert.strictEqual(event!.resource.fsPath, link.fsPath);590assert.strictEqual(event!.operation, FileOperation.DELETE);591});592593test('deleteFolder (recursive)', async () => {594return testDeleteFolderRecursive(false, false);595});596597test('deleteFolder (recursive, atomic)', async () => {598return testDeleteFolderRecursive(false, { postfix: '.vsctmp' });599});600601(isLinux /* trash is unreliable on Linux */ ? test.skip : test)('deleteFolder (recursive, useTrash)', async () => {602return testDeleteFolderRecursive(true, false);603});604605async function testDeleteFolderRecursive(useTrash: boolean, atomic: IFileAtomicOptions | false): Promise<void> {606let event: FileOperationEvent;607disposables.add(service.onDidRunOperation(e => event = e));608609const resource = URI.file(join(testDir, 'deep'));610const source = await service.resolve(resource);611612assert.strictEqual(await service.canDelete(source.resource, { recursive: true, useTrash, atomic }), true);613await service.del(source.resource, { recursive: true, useTrash, atomic });614615assert.strictEqual(existsSync(source.resource.fsPath), false);616assert.ok(event!);617assert.strictEqual(event!.resource.fsPath, resource.fsPath);618assert.strictEqual(event!.operation, FileOperation.DELETE);619}620621test('deleteFolder (non recursive)', async () => {622const resource = URI.file(join(testDir, 'deep'));623const source = await service.resolve(resource);624625assert.ok((await service.canDelete(source.resource)) instanceof Error);626627let error;628try {629await service.del(source.resource);630} catch (e) {631error = e;632}633634assert.ok(error);635});636637test('deleteFolder empty folder (recursive)', () => {638return testDeleteEmptyFolder(true);639});640641test('deleteFolder empty folder (non recursive)', () => {642return testDeleteEmptyFolder(false);643});644645async function testDeleteEmptyFolder(recursive: boolean): Promise<void> {646const { resource } = await service.createFolder(URI.file(join(testDir, 'deep', 'empty')));647648await service.del(resource, { recursive });649650assert.strictEqual(await service.exists(resource), false);651}652653test('move', async () => {654let event: FileOperationEvent;655disposables.add(service.onDidRunOperation(e => event = e));656657const source = URI.file(join(testDir, 'index.html'));658const sourceContents = readFileSync(source.fsPath);659660const target = URI.file(join(dirname(source.fsPath), 'other.html'));661662assert.strictEqual(await service.canMove(source, target), true);663const renamed = await service.move(source, target);664665assert.strictEqual(existsSync(renamed.resource.fsPath), true);666assert.strictEqual(existsSync(source.fsPath), false);667assert.ok(event!);668assert.strictEqual(event!.resource.fsPath, source.fsPath);669assert.strictEqual(event!.operation, FileOperation.MOVE);670assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);671672const targetContents = readFileSync(target.fsPath);673674assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);675assert.strictEqual(sourceContents.toString(), targetContents.toString());676});677678test('move - across providers (buffered => buffered)', async () => {679setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);680setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);681682return testMoveAcrossProviders();683});684685test('move - across providers (unbuffered => unbuffered)', async () => {686setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);687setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);688689return testMoveAcrossProviders();690});691692test('move - across providers (buffered => unbuffered)', async () => {693setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);694setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);695696return testMoveAcrossProviders();697});698699test('move - across providers (unbuffered => buffered)', async () => {700setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);701setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);702703return testMoveAcrossProviders();704});705706test('move - across providers - large (buffered => buffered)', async () => {707setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);708setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);709710return testMoveAcrossProviders('lorem.txt');711});712713test('move - across providers - large (unbuffered => unbuffered)', async () => {714setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);715setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);716717return testMoveAcrossProviders('lorem.txt');718});719720test('move - across providers - large (buffered => unbuffered)', async () => {721setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);722setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);723724return testMoveAcrossProviders('lorem.txt');725});726727test('move - across providers - large (unbuffered => buffered)', async () => {728setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);729setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);730731return testMoveAcrossProviders('lorem.txt');732});733734async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> {735let event: FileOperationEvent;736disposables.add(service.onDidRunOperation(e => event = e));737738const source = URI.file(join(testDir, sourceFile));739const sourceContents = readFileSync(source.fsPath);740741const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema });742743assert.strictEqual(await service.canMove(source, target), true);744const renamed = await service.move(source, target);745746assert.strictEqual(existsSync(renamed.resource.fsPath), true);747assert.strictEqual(existsSync(source.fsPath), false);748assert.ok(event!);749assert.strictEqual(event!.resource.fsPath, source.fsPath);750assert.strictEqual(event!.operation, FileOperation.COPY);751assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);752753const targetContents = readFileSync(target.fsPath);754755assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);756assert.strictEqual(sourceContents.toString(), targetContents.toString());757}758759test('move - multi folder', async () => {760let event: FileOperationEvent;761disposables.add(service.onDidRunOperation(e => event = e));762763const multiFolderPaths = ['a', 'couple', 'of', 'folders'];764const renameToPath = join(...multiFolderPaths, 'other.html');765766const source = URI.file(join(testDir, 'index.html'));767768assert.strictEqual(await service.canMove(source, URI.file(join(dirname(source.fsPath), renameToPath))), true);769const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), renameToPath)));770771assert.strictEqual(existsSync(renamed.resource.fsPath), true);772assert.strictEqual(existsSync(source.fsPath), false);773assert.ok(event!);774assert.strictEqual(event!.resource.fsPath, source.fsPath);775assert.strictEqual(event!.operation, FileOperation.MOVE);776assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);777});778779test('move - directory', async () => {780let event: FileOperationEvent;781disposables.add(service.onDidRunOperation(e => event = e));782783const source = URI.file(join(testDir, 'deep'));784785assert.strictEqual(await service.canMove(source, URI.file(join(dirname(source.fsPath), 'deeper'))), true);786const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'deeper')));787788assert.strictEqual(existsSync(renamed.resource.fsPath), true);789assert.strictEqual(existsSync(source.fsPath), false);790assert.ok(event!);791assert.strictEqual(event!.resource.fsPath, source.fsPath);792assert.strictEqual(event!.operation, FileOperation.MOVE);793assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);794});795796test('move - directory - across providers (buffered => buffered)', async () => {797setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);798setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);799800return testMoveFolderAcrossProviders();801});802803test('move - directory - across providers (unbuffered => unbuffered)', async () => {804setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);805setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);806807return testMoveFolderAcrossProviders();808});809810test('move - directory - across providers (buffered => unbuffered)', async () => {811setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);812setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);813814return testMoveFolderAcrossProviders();815});816817test('move - directory - across providers (unbuffered => buffered)', async () => {818setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);819setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);820821return testMoveFolderAcrossProviders();822});823824async function testMoveFolderAcrossProviders(): Promise<void> {825let event: FileOperationEvent;826disposables.add(service.onDidRunOperation(e => event = e));827828const source = URI.file(join(testDir, 'deep'));829const sourceChildren = readdirSync(source.fsPath);830831const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema });832833assert.strictEqual(await service.canMove(source, target), true);834const renamed = await service.move(source, target);835836assert.strictEqual(existsSync(renamed.resource.fsPath), true);837assert.strictEqual(existsSync(source.fsPath), false);838assert.ok(event!);839assert.strictEqual(event!.resource.fsPath, source.fsPath);840assert.strictEqual(event!.operation, FileOperation.COPY);841assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);842843const targetChildren = readdirSync(target.fsPath);844assert.strictEqual(sourceChildren.length, targetChildren.length);845for (let i = 0; i < sourceChildren.length; i++) {846assert.strictEqual(sourceChildren[i], targetChildren[i]);847}848}849850test('move - MIX CASE', async () => {851let event: FileOperationEvent;852disposables.add(service.onDidRunOperation(e => event = e));853854const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });855assert.ok(source.size > 0);856857const renamedResource = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));858assert.strictEqual(await service.canMove(source.resource, renamedResource), true);859let renamed = await service.move(source.resource, renamedResource);860861assert.strictEqual(existsSync(renamedResource.fsPath), true);862assert.strictEqual(basename(renamedResource.fsPath), 'INDEX.html');863assert.ok(event!);864assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);865assert.strictEqual(event!.operation, FileOperation.MOVE);866assert.strictEqual(event!.target!.resource.fsPath, renamedResource.fsPath);867868renamed = await service.resolve(renamedResource, { resolveMetadata: true });869assert.strictEqual(source.size, renamed.size);870});871872test('move - same file', async () => {873let event: FileOperationEvent;874disposables.add(service.onDidRunOperation(e => event = e));875876const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });877assert.ok(source.size > 0);878879assert.strictEqual(await service.canMove(source.resource, URI.file(source.resource.fsPath)), true);880let renamed = await service.move(source.resource, URI.file(source.resource.fsPath));881882assert.strictEqual(existsSync(renamed.resource.fsPath), true);883assert.strictEqual(basename(renamed.resource.fsPath), 'index.html');884assert.ok(event!);885assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);886assert.strictEqual(event!.operation, FileOperation.MOVE);887assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);888889renamed = await service.resolve(renamed.resource, { resolveMetadata: true });890assert.strictEqual(source.size, renamed.size);891});892893test('move - same file #2', async () => {894let event: FileOperationEvent;895disposables.add(service.onDidRunOperation(e => event = e));896897const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });898assert.ok(source.size > 0);899900const targetParent = URI.file(testDir);901const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });902903assert.strictEqual(await service.canMove(source.resource, target), true);904let renamed = await service.move(source.resource, target);905906assert.strictEqual(existsSync(renamed.resource.fsPath), true);907assert.strictEqual(basename(renamed.resource.fsPath), 'index.html');908assert.ok(event!);909assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);910assert.strictEqual(event!.operation, FileOperation.MOVE);911assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);912913renamed = await service.resolve(renamed.resource, { resolveMetadata: true });914assert.strictEqual(source.size, renamed.size);915});916917test('move - source parent of target', async () => {918let event: FileOperationEvent;919disposables.add(service.onDidRunOperation(e => event = e));920921let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });922const originalSize = source.size;923assert.ok(originalSize > 0);924925assert.ok((await service.canMove(URI.file(testDir), URI.file(join(testDir, 'binary.txt'))) instanceof Error));926927let error;928try {929await service.move(URI.file(testDir), URI.file(join(testDir, 'binary.txt')));930} catch (e) {931error = e;932}933934assert.ok(error);935assert.ok(!event!);936937source = await service.resolve(source.resource, { resolveMetadata: true });938assert.strictEqual(originalSize, source.size);939});940941test('move - FILE_MOVE_CONFLICT', async () => {942let event: FileOperationEvent;943disposables.add(service.onDidRunOperation(e => event = e));944945let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });946const originalSize = source.size;947assert.ok(originalSize > 0);948949assert.ok((await service.canMove(source.resource, URI.file(join(testDir, 'binary.txt'))) instanceof Error));950951let error;952try {953await service.move(source.resource, URI.file(join(testDir, 'binary.txt')));954} catch (e) {955error = e;956}957958assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT);959assert.ok(!event!);960961source = await service.resolve(source.resource, { resolveMetadata: true });962assert.strictEqual(originalSize, source.size);963});964965test('move - overwrite folder with file', async () => {966let createEvent: FileOperationEvent;967let moveEvent: FileOperationEvent;968let deleteEvent: FileOperationEvent;969disposables.add(service.onDidRunOperation(e => {970if (e.operation === FileOperation.CREATE) {971createEvent = e;972} else if (e.operation === FileOperation.DELETE) {973deleteEvent = e;974} else if (e.operation === FileOperation.MOVE) {975moveEvent = e;976}977}));978979const parent = await service.resolve(URI.file(testDir));980const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));981const f = await service.createFolder(folderResource);982const source = URI.file(join(testDir, 'deep', 'conway.js'));983984assert.strictEqual(await service.canMove(source, f.resource, true), true);985const moved = await service.move(source, f.resource, true);986987assert.strictEqual(existsSync(moved.resource.fsPath), true);988assert.ok(statSync(moved.resource.fsPath).isFile);989assert.ok(createEvent!);990assert.ok(deleteEvent!);991assert.ok(moveEvent!);992assert.strictEqual(moveEvent!.resource.fsPath, source.fsPath);993assert.strictEqual(moveEvent!.target!.resource.fsPath, moved.resource.fsPath);994assert.strictEqual(deleteEvent!.resource.fsPath, folderResource.fsPath);995});996997test('copy', async () => {998await doTestCopy();999});10001001test('copy - unbuffered (FileSystemProviderCapabilities.FileReadWrite)', async () => {1002setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);10031004await doTestCopy();1005});10061007test('copy - unbuffered large (FileSystemProviderCapabilities.FileReadWrite)', async () => {1008setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);10091010await doTestCopy('lorem.txt');1011});10121013test('copy - buffered (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {1014setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);10151016await doTestCopy();1017});10181019test('copy - buffered large (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {1020setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);10211022await doTestCopy('lorem.txt');1023});10241025function setCapabilities(provider: TestDiskFileSystemProvider, capabilities: FileSystemProviderCapabilities): void {1026provider.capabilities = capabilities;1027if (isLinux) {1028provider.capabilities |= FileSystemProviderCapabilities.PathCaseSensitive;1029}1030}10311032async function doTestCopy(sourceName: string = 'index.html') {1033let event: FileOperationEvent;1034disposables.add(service.onDidRunOperation(e => event = e));10351036const source = await service.resolve(URI.file(join(testDir, sourceName)));1037const target = URI.file(join(testDir, 'other.html'));10381039assert.strictEqual(await service.canCopy(source.resource, target), true);1040const copied = await service.copy(source.resource, target);10411042assert.strictEqual(existsSync(copied.resource.fsPath), true);1043assert.strictEqual(existsSync(source.resource.fsPath), true);1044assert.ok(event!);1045assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);1046assert.strictEqual(event!.operation, FileOperation.COPY);1047assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);10481049const sourceContents = readFileSync(source.resource.fsPath);1050const targetContents = readFileSync(target.fsPath);10511052assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);1053assert.strictEqual(sourceContents.toString(), targetContents.toString());1054}10551056test('copy - overwrite folder with file', async () => {1057let createEvent: FileOperationEvent;1058let copyEvent: FileOperationEvent;1059let deleteEvent: FileOperationEvent;1060disposables.add(service.onDidRunOperation(e => {1061if (e.operation === FileOperation.CREATE) {1062createEvent = e;1063} else if (e.operation === FileOperation.DELETE) {1064deleteEvent = e;1065} else if (e.operation === FileOperation.COPY) {1066copyEvent = e;1067}1068}));10691070const parent = await service.resolve(URI.file(testDir));1071const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));1072const f = await service.createFolder(folderResource);1073const source = URI.file(join(testDir, 'deep', 'conway.js'));10741075assert.strictEqual(await service.canCopy(source, f.resource, true), true);1076const copied = await service.copy(source, f.resource, true);10771078assert.strictEqual(existsSync(copied.resource.fsPath), true);1079assert.ok(statSync(copied.resource.fsPath).isFile);1080assert.ok(createEvent!);1081assert.ok(deleteEvent!);1082assert.ok(copyEvent!);1083assert.strictEqual(copyEvent!.resource.fsPath, source.fsPath);1084assert.strictEqual(copyEvent!.target!.resource.fsPath, copied.resource.fsPath);1085assert.strictEqual(deleteEvent!.resource.fsPath, folderResource.fsPath);1086});10871088test('copy - MIX CASE same target - no overwrite', async () => {1089let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1090const originalSize = source.size;1091assert.ok(originalSize > 0);10921093const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));10941095const canCopy = await service.canCopy(source.resource, target);10961097let error;1098let copied: IFileStatWithMetadata;1099try {1100copied = await service.copy(source.resource, target);1101} catch (e) {1102error = e;1103}11041105if (isLinux) {1106assert.ok(!error);1107assert.strictEqual(canCopy, true);11081109assert.strictEqual(existsSync(copied!.resource.fsPath), true);1110assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));1111assert.strictEqual(source.size, copied!.size);1112} else {1113assert.ok(error);1114assert.ok(canCopy instanceof Error);11151116source = await service.resolve(source.resource, { resolveMetadata: true });1117assert.strictEqual(originalSize, source.size);1118}1119});11201121test('copy - MIX CASE same target - overwrite', async () => {1122let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1123const originalSize = source.size;1124assert.ok(originalSize > 0);11251126const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));11271128const canCopy = await service.canCopy(source.resource, target, true);11291130let error;1131let copied: IFileStatWithMetadata;1132try {1133copied = await service.copy(source.resource, target, true);1134} catch (e) {1135error = e;1136}11371138if (isLinux) {1139assert.ok(!error);1140assert.strictEqual(canCopy, true);11411142assert.strictEqual(existsSync(copied!.resource.fsPath), true);1143assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));1144assert.strictEqual(source.size, copied!.size);1145} else {1146assert.ok(error);1147assert.ok(canCopy instanceof Error);11481149source = await service.resolve(source.resource, { resolveMetadata: true });1150assert.strictEqual(originalSize, source.size);1151}1152});11531154test('copy - MIX CASE different target - overwrite', async () => {1155const source1 = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1156assert.ok(source1.size > 0);11571158const renamed = await service.move(source1.resource, URI.file(join(dirname(source1.resource.fsPath), 'CONWAY.js')));1159assert.strictEqual(existsSync(renamed.resource.fsPath), true);1160assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js'));1161assert.strictEqual(source1.size, renamed.size);11621163const source2 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')), { resolveMetadata: true });1164const target = URI.file(join(testDir, basename(source2.resource.path)));11651166assert.strictEqual(await service.canCopy(source2.resource, target, true), true);1167const res = await service.copy(source2.resource, target, true);1168assert.strictEqual(existsSync(res.resource.fsPath), true);1169assert.ok(readdirSync(testDir).some(f => f === 'conway.js'));1170assert.strictEqual(source2.size, res.size);1171});11721173test('copy - same file', async () => {1174let event: FileOperationEvent;1175disposables.add(service.onDidRunOperation(e => event = e));11761177const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1178assert.ok(source.size > 0);11791180assert.strictEqual(await service.canCopy(source.resource, URI.file(source.resource.fsPath)), true);1181let copied = await service.copy(source.resource, URI.file(source.resource.fsPath));11821183assert.strictEqual(existsSync(copied.resource.fsPath), true);1184assert.strictEqual(basename(copied.resource.fsPath), 'index.html');1185assert.ok(event!);1186assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);1187assert.strictEqual(event!.operation, FileOperation.COPY);1188assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);11891190copied = await service.resolve(source.resource, { resolveMetadata: true });1191assert.strictEqual(source.size, copied.size);1192});11931194test('copy - same file #2', async () => {1195let event: FileOperationEvent;1196disposables.add(service.onDidRunOperation(e => event = e));11971198const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1199assert.ok(source.size > 0);12001201const targetParent = URI.file(testDir);1202const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });12031204assert.strictEqual(await service.canCopy(source.resource, URI.file(target.fsPath)), true);1205let copied = await service.copy(source.resource, URI.file(target.fsPath));12061207assert.strictEqual(existsSync(copied.resource.fsPath), true);1208assert.strictEqual(basename(copied.resource.fsPath), 'index.html');1209assert.ok(event!);1210assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);1211assert.strictEqual(event!.operation, FileOperation.COPY);1212assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);12131214copied = await service.resolve(source.resource, { resolveMetadata: true });1215assert.strictEqual(source.size, copied.size);1216});12171218test('cloneFile - basics', () => {1219return testCloneFile();1220});12211222test('cloneFile - via copy capability', () => {1223setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileFolderCopy);12241225return testCloneFile();1226});12271228test('cloneFile - via pipe', () => {1229setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);12301231return testCloneFile();1232});12331234async function testCloneFile(): Promise<void> {1235const source1 = URI.file(join(testDir, 'index.html'));1236const source1Size = (await service.resolve(source1, { resolveMetadata: true })).size;12371238const source2 = URI.file(join(testDir, 'lorem.txt'));1239const source2Size = (await service.resolve(source2, { resolveMetadata: true })).size;12401241const targetParent = URI.file(testDir);12421243// same path is a no-op1244await service.cloneFile(source1, source1);12451246// simple clone to existing parent folder path1247const target1 = targetParent.with({ path: posix.join(targetParent.path, `${posix.basename(source1.path)}-clone`) });12481249await service.cloneFile(source1, URI.file(target1.fsPath));12501251assert.strictEqual(existsSync(target1.fsPath), true);1252assert.strictEqual(basename(target1.fsPath), 'index.html-clone');12531254let target1Size = (await service.resolve(target1, { resolveMetadata: true })).size;12551256assert.strictEqual(source1Size, target1Size);12571258// clone to same path overwrites1259await service.cloneFile(source2, URI.file(target1.fsPath));12601261target1Size = (await service.resolve(target1, { resolveMetadata: true })).size;12621263assert.strictEqual(source2Size, target1Size);1264assert.notStrictEqual(source1Size, target1Size);12651266// clone creates missing folders ad-hoc1267const target2 = targetParent.with({ path: posix.join(targetParent.path, 'foo', 'bar', `${posix.basename(source1.path)}-clone`) });12681269await service.cloneFile(source1, URI.file(target2.fsPath));12701271assert.strictEqual(existsSync(target2.fsPath), true);1272assert.strictEqual(basename(target2.fsPath), 'index.html-clone');12731274const target2Size = (await service.resolve(target2, { resolveMetadata: true })).size;12751276assert.strictEqual(source1Size, target2Size);1277}12781279test('readFile - small file - default', () => {1280return testReadFile(URI.file(join(testDir, 'small.txt')));1281});12821283test('readFile - small file - buffered', () => {1284setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);12851286return testReadFile(URI.file(join(testDir, 'small.txt')));1287});12881289test('readFile - small file - buffered / readonly', () => {1290setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);12911292return testReadFile(URI.file(join(testDir, 'small.txt')));1293});12941295test('readFile - small file - unbuffered', async () => {1296setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);12971298return testReadFile(URI.file(join(testDir, 'small.txt')));1299});13001301test('readFile - small file - unbuffered / readonly', async () => {1302setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);13031304return testReadFile(URI.file(join(testDir, 'small.txt')));1305});13061307test('readFile - small file - streamed', async () => {1308setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13091310return testReadFile(URI.file(join(testDir, 'small.txt')));1311});13121313test('readFile - small file - streamed / readonly', async () => {1314setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream | FileSystemProviderCapabilities.Readonly);13151316return testReadFile(URI.file(join(testDir, 'small.txt')));1317});13181319test('readFile - large file - default', async () => {1320return testReadFile(URI.file(join(testDir, 'lorem.txt')));1321});13221323test('readFile - large file - buffered', async () => {1324setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);13251326return testReadFile(URI.file(join(testDir, 'lorem.txt')));1327});13281329test('readFile - large file - unbuffered', async () => {1330setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);13311332return testReadFile(URI.file(join(testDir, 'lorem.txt')));1333});13341335test('readFile - large file - streamed', async () => {1336setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13371338return testReadFile(URI.file(join(testDir, 'lorem.txt')));1339});13401341test('readFile - atomic (emulated on service level)', async () => {1342setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13431344return testReadFile(URI.file(join(testDir, 'lorem.txt')), { atomic: true });1345});13461347test('readFile - atomic (natively supported)', async () => {1348setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite & FileSystemProviderCapabilities.FileAtomicRead);13491350return testReadFile(URI.file(join(testDir, 'lorem.txt')), { atomic: true });1351});13521353async function testReadFile(resource: URI, options?: IReadFileOptions): Promise<void> {1354const content = await service.readFile(resource, options);13551356assert.strictEqual(content.value.toString(), readFileSync(resource.fsPath).toString());1357}13581359test('readFileStream - small file - default', () => {1360return testReadFileStream(URI.file(join(testDir, 'small.txt')));1361});13621363test('readFileStream - small file - buffered', () => {1364setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);13651366return testReadFileStream(URI.file(join(testDir, 'small.txt')));1367});13681369test('readFileStream - small file - unbuffered', async () => {1370setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);13711372return testReadFileStream(URI.file(join(testDir, 'small.txt')));1373});13741375test('readFileStream - small file - streamed', async () => {1376setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13771378return testReadFileStream(URI.file(join(testDir, 'small.txt')));1379});13801381async function testReadFileStream(resource: URI): Promise<void> {1382const content = await service.readFileStream(resource);13831384assert.strictEqual((await streamToBuffer(content.value)).toString(), readFileSync(resource.fsPath).toString());1385}13861387test('readFile - Files are intermingled #38331 - default', async () => {1388return testFilesNotIntermingled();1389});13901391test('readFile - Files are intermingled #38331 - buffered', async () => {1392setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);13931394return testFilesNotIntermingled();1395});13961397test('readFile - Files are intermingled #38331 - unbuffered', async () => {1398setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);13991400return testFilesNotIntermingled();1401});14021403test('readFile - Files are intermingled #38331 - streamed', async () => {1404setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);14051406return testFilesNotIntermingled();1407});14081409async function testFilesNotIntermingled() {1410const resource1 = URI.file(join(testDir, 'lorem.txt'));1411const resource2 = URI.file(join(testDir, 'some_utf16le.css'));14121413// load in sequence and keep data1414const value1 = await service.readFile(resource1);1415const value2 = await service.readFile(resource2);14161417// load in parallel in expect the same result1418const result = await Promise.all([1419service.readFile(resource1),1420service.readFile(resource2)1421]);14221423assert.strictEqual(result[0].value.toString(), value1.value.toString());1424assert.strictEqual(result[1].value.toString(), value2.value.toString());1425}14261427test('readFile - from position (ASCII) - default', async () => {1428return testReadFileFromPositionAscii();1429});14301431test('readFile - from position (ASCII) - buffered', async () => {1432setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);14331434return testReadFileFromPositionAscii();1435});14361437test('readFile - from position (ASCII) - unbuffered', async () => {1438setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);14391440return testReadFileFromPositionAscii();1441});14421443test('readFile - from position (ASCII) - streamed', async () => {1444setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);14451446return testReadFileFromPositionAscii();1447});14481449async function testReadFileFromPositionAscii() {1450const resource = URI.file(join(testDir, 'small.txt'));14511452const contents = await service.readFile(resource, { position: 6 });14531454assert.strictEqual(contents.value.toString(), 'File');1455}14561457test('readFile - from position (with umlaut) - default', async () => {1458return testReadFileFromPositionUmlaut();1459});14601461test('readFile - from position (with umlaut) - buffered', async () => {1462setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);14631464return testReadFileFromPositionUmlaut();1465});14661467test('readFile - from position (with umlaut) - unbuffered', async () => {1468setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);14691470return testReadFileFromPositionUmlaut();1471});14721473test('readFile - from position (with umlaut) - streamed', async () => {1474setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);14751476return testReadFileFromPositionUmlaut();1477});14781479async function testReadFileFromPositionUmlaut() {1480const resource = URI.file(join(testDir, 'small_umlaut.txt'));14811482const contents = await service.readFile(resource, { position: Buffer.from('Small File with Ü').length });14831484assert.strictEqual(contents.value.toString(), 'mlaut');1485}14861487test('readFile - 3 bytes (ASCII) - default', async () => {1488return testReadThreeBytesFromFile();1489});14901491test('readFile - 3 bytes (ASCII) - buffered', async () => {1492setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);14931494return testReadThreeBytesFromFile();1495});14961497test('readFile - 3 bytes (ASCII) - unbuffered', async () => {1498setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);14991500return testReadThreeBytesFromFile();1501});15021503test('readFile - 3 bytes (ASCII) - streamed', async () => {1504setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);15051506return testReadThreeBytesFromFile();1507});15081509async function testReadThreeBytesFromFile() {1510const resource = URI.file(join(testDir, 'small.txt'));15111512const contents = await service.readFile(resource, { length: 3 });15131514assert.strictEqual(contents.value.toString(), 'Sma');1515}15161517test('readFile - 20000 bytes (large) - default', async () => {1518return readLargeFileWithLength(20000);1519});15201521test('readFile - 20000 bytes (large) - buffered', async () => {1522setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);15231524return readLargeFileWithLength(20000);1525});15261527test('readFile - 20000 bytes (large) - unbuffered', async () => {1528setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);15291530return readLargeFileWithLength(20000);1531});15321533test('readFile - 20000 bytes (large) - streamed', async () => {1534setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);15351536return readLargeFileWithLength(20000);1537});15381539test('readFile - 80000 bytes (large) - default', async () => {1540return readLargeFileWithLength(80000);1541});15421543test('readFile - 80000 bytes (large) - buffered', async () => {1544setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);15451546return readLargeFileWithLength(80000);1547});15481549test('readFile - 80000 bytes (large) - unbuffered', async () => {1550setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);15511552return readLargeFileWithLength(80000);1553});15541555test('readFile - 80000 bytes (large) - streamed', async () => {1556setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);15571558return readLargeFileWithLength(80000);1559});15601561async function readLargeFileWithLength(length: number) {1562const resource = URI.file(join(testDir, 'lorem.txt'));15631564const contents = await service.readFile(resource, { length });15651566assert.strictEqual(contents.value.byteLength, length);1567}15681569test('readFile - FILE_IS_DIRECTORY', async () => {1570const resource = URI.file(join(testDir, 'deep'));15711572let error: FileOperationError | undefined = undefined;1573try {1574await service.readFile(resource);1575} catch (err) {1576error = err;1577}15781579assert.ok(error);1580assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_IS_DIRECTORY);1581});15821583(isWindows /* error code does not seem to be supported on windows */ ? test.skip : test)('readFile - FILE_NOT_DIRECTORY', async () => {1584const resource = URI.file(join(testDir, 'lorem.txt', 'file.txt'));15851586let error: FileOperationError | undefined = undefined;1587try {1588await service.readFile(resource);1589} catch (err) {1590error = err;1591}15921593assert.ok(error);1594assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_DIRECTORY);1595});15961597test('readFile - FILE_NOT_FOUND', async () => {1598const resource = URI.file(join(testDir, '404.html'));15991600let error: FileOperationError | undefined = undefined;1601try {1602await service.readFile(resource);1603} catch (err) {1604error = err;1605}16061607assert.ok(error);1608assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_FOUND);1609});16101611test('readFile - FILE_NOT_MODIFIED_SINCE - default', async () => {1612return testNotModifiedSince();1613});16141615test('readFile - FILE_NOT_MODIFIED_SINCE - buffered', async () => {1616setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);16171618return testNotModifiedSince();1619});16201621test('readFile - FILE_NOT_MODIFIED_SINCE - unbuffered', async () => {1622setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);16231624return testNotModifiedSince();1625});16261627test('readFile - FILE_NOT_MODIFIED_SINCE - streamed', async () => {1628setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);16291630return testNotModifiedSince();1631});16321633async function testNotModifiedSince() {1634const resource = URI.file(join(testDir, 'index.html'));16351636const contents = await service.readFile(resource);1637fileProvider.totalBytesRead = 0;16381639let error: FileOperationError | undefined = undefined;1640try {1641await service.readFile(resource, { etag: contents.etag });1642} catch (err) {1643error = err;1644}16451646assert.ok(error);1647assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);1648assert.ok(error instanceof NotModifiedSinceFileOperationError && error.stat);1649assert.strictEqual(fileProvider.totalBytesRead, 0);1650}16511652test('readFile - FILE_NOT_MODIFIED_SINCE does not fire wrongly - https://github.com/microsoft/vscode/issues/72909', async () => {1653fileProvider.setInvalidStatSize(true);16541655const resource = URI.file(join(testDir, 'index.html'));16561657await service.readFile(resource);16581659let error: FileOperationError | undefined = undefined;1660try {1661await service.readFile(resource, { etag: undefined });1662} catch (err) {1663error = err;1664}16651666assert.ok(!error);1667});16681669test('readFile - FILE_TOO_LARGE - default', async () => {1670return testFileTooLarge();1671});16721673test('readFile - FILE_TOO_LARGE - buffered', async () => {1674setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);16751676return testFileTooLarge();1677});16781679test('readFile - FILE_TOO_LARGE - unbuffered', async () => {1680setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);16811682return testFileTooLarge();1683});16841685test('readFile - FILE_TOO_LARGE - streamed', async () => {1686setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);16871688return testFileTooLarge();1689});16901691async function testFileTooLarge() {1692await doTestFileTooLarge(false);16931694// Also test when the stat size is wrong1695fileProvider.setSmallStatSize(true);1696return doTestFileTooLarge(true);1697}16981699async function doTestFileTooLarge(statSizeWrong: boolean) {1700const resource = URI.file(join(testDir, 'index.html'));17011702let error: FileOperationError | undefined = undefined;1703try {1704await service.readFile(resource, { limits: { size: 10 } });1705} catch (err) {1706error = err;1707}17081709if (!statSizeWrong) {1710assert.ok(error instanceof TooLargeFileOperationError);1711assert.ok(typeof error.size === 'number');1712}1713assert.strictEqual(error!.fileOperationResult, FileOperationResult.FILE_TOO_LARGE);1714}17151716(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('readFile - dangling symbolic link - https://github.com/microsoft/vscode/issues/116049', async () => {1717const link = URI.file(join(testDir, 'small.js-link'));1718await promises.symlink(join(testDir, 'small.js'), link.fsPath);17191720let error: FileOperationError | undefined = undefined;1721try {1722await service.readFile(link);1723} catch (err) {1724error = err;1725}17261727assert.ok(error);1728});17291730test('createFile', async () => {1731return assertCreateFile(contents => VSBuffer.fromString(contents));1732});17331734test('createFile (readable)', async () => {1735return assertCreateFile(contents => bufferToReadable(VSBuffer.fromString(contents)));1736});17371738test('createFile (stream)', async () => {1739return assertCreateFile(contents => bufferToStream(VSBuffer.fromString(contents)));1740});17411742async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> {1743let event: FileOperationEvent;1744disposables.add(service.onDidRunOperation(e => event = e));17451746const contents = 'Hello World';1747const resource = URI.file(join(testDir, 'test.txt'));17481749assert.strictEqual(await service.canCreateFile(resource), true);1750const fileStat = await service.createFile(resource, converter(contents));1751assert.strictEqual(fileStat.name, 'test.txt');1752assert.strictEqual(existsSync(fileStat.resource.fsPath), true);1753assert.strictEqual(readFileSync(fileStat.resource.fsPath).toString(), contents);17541755assert.ok(event!);1756assert.strictEqual(event!.resource.fsPath, resource.fsPath);1757assert.strictEqual(event!.operation, FileOperation.CREATE);1758assert.strictEqual(event!.target!.resource.fsPath, resource.fsPath);1759}17601761test('createFile (does not overwrite by default)', async () => {1762const contents = 'Hello World';1763const resource = URI.file(join(testDir, 'test.txt'));17641765writeFileSync(resource.fsPath, ''); // create file17661767assert.ok((await service.canCreateFile(resource)) instanceof Error);17681769let error;1770try {1771await service.createFile(resource, VSBuffer.fromString(contents));1772} catch (err) {1773error = err;1774}17751776assert.ok(error);1777});17781779test('createFile (allows to overwrite existing)', async () => {1780let event: FileOperationEvent;1781disposables.add(service.onDidRunOperation(e => event = e));17821783const contents = 'Hello World';1784const resource = URI.file(join(testDir, 'test.txt'));17851786writeFileSync(resource.fsPath, ''); // create file17871788assert.strictEqual(await service.canCreateFile(resource, { overwrite: true }), true);1789const fileStat = await service.createFile(resource, VSBuffer.fromString(contents), { overwrite: true });1790assert.strictEqual(fileStat.name, 'test.txt');1791assert.strictEqual(existsSync(fileStat.resource.fsPath), true);1792assert.strictEqual(readFileSync(fileStat.resource.fsPath).toString(), contents);17931794assert.ok(event!);1795assert.strictEqual(event!.resource.fsPath, resource.fsPath);1796assert.strictEqual(event!.operation, FileOperation.CREATE);1797assert.strictEqual(event!.target!.resource.fsPath, resource.fsPath);1798});17991800test('writeFile - default', async () => {1801return testWriteFile(false);1802});18031804test('writeFile - flush on write', async () => {1805DiskFileSystemProvider.configureFlushOnWrite(true);1806try {1807return await testWriteFile(false);1808} finally {1809DiskFileSystemProvider.configureFlushOnWrite(false);1810}1811});18121813test('writeFile - buffered', async () => {1814setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);18151816return testWriteFile(false);1817});18181819test('writeFile - unbuffered', async () => {1820setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);18211822return testWriteFile(false);1823});18241825test('writeFile - default (atomic)', async () => {1826return testWriteFile(true);1827});18281829test('writeFile - flush on write (atomic)', async () => {1830DiskFileSystemProvider.configureFlushOnWrite(true);1831try {1832return await testWriteFile(true);1833} finally {1834DiskFileSystemProvider.configureFlushOnWrite(false);1835}1836});18371838test('writeFile - buffered (atomic)', async () => {1839setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAtomicWrite);18401841let e;1842try {1843await testWriteFile(true);1844} catch (error) {1845e = error;1846}18471848assert.ok(e);1849});18501851test('writeFile - unbuffered (atomic)', async () => {1852setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);18531854return testWriteFile(true);1855});18561857(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('writeFile - atomic writing does not break symlinks', async () => {1858const link = URI.file(join(testDir, 'lorem.txt-linked'));1859await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath);18601861const content = 'Updates to the lorem file';1862await service.writeFile(link, VSBuffer.fromString(content), { atomic: { postfix: '.vsctmp' } });1863assert.strictEqual(readFileSync(link.fsPath).toString(), content);18641865const resolved = await service.resolve(link);1866assert.strictEqual(resolved.isSymbolicLink, true);1867});18681869async function testWriteFile(atomic: boolean) {1870let event: FileOperationEvent;1871disposables.add(service.onDidRunOperation(e => event = e));18721873const resource = URI.file(join(testDir, 'small.txt'));18741875const content = readFileSync(resource.fsPath).toString();1876assert.strictEqual(content, 'Small File');18771878const newContent = 'Updates to the small file';1879await service.writeFile(resource, VSBuffer.fromString(newContent), { atomic: atomic ? { postfix: '.vsctmp' } : false });18801881assert.ok(event!);1882assert.strictEqual(event!.resource.fsPath, resource.fsPath);1883assert.strictEqual(event!.operation, FileOperation.WRITE);18841885assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);1886}18871888test('writeFile (large file) - default', async () => {1889return testWriteFileLarge(false);1890});18911892test('writeFile (large file) - buffered', async () => {1893setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);18941895return testWriteFileLarge(false);1896});18971898test('writeFile (large file) - unbuffered', async () => {1899setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);19001901return testWriteFileLarge(false);1902});19031904test('writeFile (large file) - default (atomic)', async () => {1905return testWriteFileLarge(true);1906});19071908test('writeFile (large file) - buffered (atomic)', async () => {1909setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAtomicWrite);19101911let e;1912try {1913await testWriteFileLarge(true);1914} catch (error) {1915e = error;1916}19171918assert.ok(e);1919});19201921test('writeFile (large file) - unbuffered (atomic)', async () => {1922setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);19231924return testWriteFileLarge(true);1925});19261927async function testWriteFileLarge(atomic: boolean) {1928const resource = URI.file(join(testDir, 'lorem.txt'));19291930const content = readFileSync(resource.fsPath);1931const newContent = content.toString() + content.toString();19321933const fileStat = await service.writeFile(resource, VSBuffer.fromString(newContent), { atomic: atomic ? { postfix: '.vsctmp' } : false });1934assert.strictEqual(fileStat.name, 'lorem.txt');19351936assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);1937}19381939test('writeFile (large file) - unbuffered (atomic) - concurrent writes with multiple services', async () => {1940setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);19411942const resource = URI.file(join(testDir, 'lorem.txt'));19431944const content = readFileSync(resource.fsPath);1945const newContent = content.toString() + content.toString();19461947const promises: Promise<IFileStatWithMetadata>[] = [];1948let suffix = 0;1949for (let i = 0; i < 10; i++) {1950const service = disposables.add(new FileService(new NullLogService()));1951disposables.add(service.registerProvider(Schemas.file, fileProvider));19521953promises.push(service.writeFile(resource, VSBuffer.fromString(`${newContent}${++suffix}`), { atomic: { postfix: '.vsctmp' } }));1954await timeout(0);1955}19561957await Promise.allSettled(promises);19581959assert.strictEqual(readFileSync(resource.fsPath).toString(), `${newContent}${suffix}`);1960});19611962test('writeFile - buffered - readonly throws', async () => {1963setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);19641965return testWriteFileReadonlyThrows();1966});19671968test('writeFile - unbuffered - readonly throws', async () => {1969setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);19701971return testWriteFileReadonlyThrows();1972});19731974async function testWriteFileReadonlyThrows() {1975const resource = URI.file(join(testDir, 'small.txt'));19761977const content = readFileSync(resource.fsPath).toString();1978assert.strictEqual(content, 'Small File');19791980const newContent = 'Updates to the small file';19811982let error: Error;1983try {1984await service.writeFile(resource, VSBuffer.fromString(newContent));1985} catch (err) {1986error = err;1987}19881989assert.ok(error!);1990}19911992test('writeFile (large file) - multiple parallel writes queue up and atomic read support (via file service)', async () => {1993const resource = URI.file(join(testDir, 'lorem.txt'));19941995const content = readFileSync(resource.fsPath);1996const newContent = content.toString() + content.toString();19971998const writePromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {1999const fileStat = await service.writeFile(resource, VSBuffer.fromString(offset + newContent));2000assert.strictEqual(fileStat.name, 'lorem.txt');2001}));20022003const readPromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async () => {2004const fileContent = await service.readFile(resource, { atomic: true });2005assert.ok(fileContent.value.byteLength > 0); // `atomic: true` ensures we never read a truncated file2006}));20072008await Promise.all([writePromises, readPromises]);2009});20102011test('provider - write barrier prevents dirty writes', async () => {2012const resource = URI.file(join(testDir, 'lorem.txt'));20132014const content = readFileSync(resource.fsPath);2015const newContent = content.toString() + content.toString();20162017const provider = service.getProvider(resource.scheme);2018assert.ok(provider);2019assert.ok(hasOpenReadWriteCloseCapability(provider));20202021const writePromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {2022const content = offset + newContent;2023const contentBuffer = VSBuffer.fromString(content).buffer;20242025const fd = await provider.open(resource, { create: true, unlock: false });2026try {2027await provider.write(fd, 0, VSBuffer.fromString(content).buffer, 0, contentBuffer.byteLength);20282029// Here since `close` is not called, all other writes are2030// waiting on the barrier to release, so doing a readFile2031// should give us a consistent view of the file contents2032assert.strictEqual((await promises.readFile(resource.fsPath)).toString(), content);2033} finally {2034await provider.close(fd);2035}2036}));20372038await Promise.all([writePromises]);2039});20402041test('provider - write barrier is partitioned per resource', async () => {2042const resource1 = URI.file(join(testDir, 'lorem.txt'));2043const resource2 = URI.file(join(testDir, 'test.txt'));20442045const provider = service.getProvider(resource1.scheme);2046assert.ok(provider);2047assert.ok(hasOpenReadWriteCloseCapability(provider));20482049const fd1 = await provider.open(resource1, { create: true, unlock: false });2050const fd2 = await provider.open(resource2, { create: true, unlock: false });20512052const newContent = 'Hello World';20532054try {2055await provider.write(fd1, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength);2056assert.strictEqual((await promises.readFile(resource1.fsPath)).toString(), newContent);20572058await provider.write(fd2, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength);2059assert.strictEqual((await promises.readFile(resource2.fsPath)).toString(), newContent);2060} finally {2061await Promise.allSettled([2062await provider.close(fd1),2063await provider.close(fd2)2064]);2065}2066});20672068test('provider - write barrier not becoming stale', async () => {2069const newFolder = join(testDir, 'new-folder');2070const newResource = URI.file(join(newFolder, 'lorem.txt'));20712072const provider = service.getProvider(newResource.scheme);2073assert.ok(provider);2074assert.ok(hasOpenReadWriteCloseCapability(provider));20752076let error: Error | undefined = undefined;2077try {2078await provider.open(newResource, { create: true, unlock: false });2079} catch (e) {2080error = e;2081}20822083assert.ok(error); // expected because `new-folder` does not exist20842085await promises.mkdir(newFolder);20862087const content = readFileSync(URI.file(join(testDir, 'lorem.txt')).fsPath);2088const newContent = content.toString() + content.toString();2089const newContentBuffer = VSBuffer.fromString(newContent).buffer;20902091const fd = await provider.open(newResource, { create: true, unlock: false });2092try {2093await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);20942095assert.strictEqual((await promises.readFile(newResource.fsPath)).toString(), newContent);2096} finally {2097await provider.close(fd);2098}2099});21002101test('provider - atomic reads (write pending when read starts)', async () => {2102const resource = URI.file(join(testDir, 'lorem.txt'));21032104const content = readFileSync(resource.fsPath);2105const newContent = content.toString() + content.toString();2106const newContentBuffer = VSBuffer.fromString(newContent).buffer;21072108const provider = service.getProvider(resource.scheme);2109assert.ok(provider);2110assert.ok(hasOpenReadWriteCloseCapability(provider));2111assert.ok(hasFileAtomicReadCapability(provider));21122113let atomicReadPromise: Promise<Uint8Array> | undefined = undefined;2114const fd = await provider.open(resource, { create: true, unlock: false });2115try {21162117// Start reading while write is pending2118atomicReadPromise = provider.readFile(resource, { atomic: true });21192120// Simulate a slow write, giving the read2121// a chance to succeed if it were not atomic2122await timeout(20);21232124await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);2125} finally {2126await provider.close(fd);2127}21282129assert.ok(atomicReadPromise);21302131const atomicReadResult = await atomicReadPromise;2132assert.strictEqual(atomicReadResult.byteLength, newContentBuffer.byteLength);2133});21342135test('provider - atomic reads (read pending when write starts)', async () => {2136const resource = URI.file(join(testDir, 'lorem.txt'));21372138const content = readFileSync(resource.fsPath);2139const newContent = content.toString() + content.toString();2140const newContentBuffer = VSBuffer.fromString(newContent).buffer;21412142const provider = service.getProvider(resource.scheme);2143assert.ok(provider);2144assert.ok(hasOpenReadWriteCloseCapability(provider));2145assert.ok(hasFileAtomicReadCapability(provider));21462147let atomicReadPromise = provider.readFile(resource, { atomic: true });21482149const fdPromise = provider.open(resource, { create: true, unlock: false }).then(async fd => {2150try {2151return await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);2152} finally {2153await provider.close(fd);2154}2155});21562157let atomicReadResult = await atomicReadPromise;2158assert.strictEqual(atomicReadResult.byteLength, content.byteLength);21592160await fdPromise;21612162atomicReadPromise = provider.readFile(resource, { atomic: true });2163atomicReadResult = await atomicReadPromise;2164assert.strictEqual(atomicReadResult.byteLength, newContentBuffer.byteLength);2165});21662167test('writeFile (readable) - default', async () => {2168return testWriteFileReadable();2169});21702171test('writeFile (readable) - buffered', async () => {2172setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);21732174return testWriteFileReadable();2175});21762177test('writeFile (readable) - unbuffered', async () => {2178setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);21792180return testWriteFileReadable();2181});21822183async function testWriteFileReadable() {2184const resource = URI.file(join(testDir, 'small.txt'));21852186const content = readFileSync(resource.fsPath).toString();2187assert.strictEqual(content, 'Small File');21882189const newContent = 'Updates to the small file';2190await service.writeFile(resource, toLineByLineReadable(newContent));21912192assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);2193}21942195test('writeFile (large file - readable) - default', async () => {2196return testWriteFileLargeReadable();2197});21982199test('writeFile (large file - readable) - buffered', async () => {2200setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);22012202return testWriteFileLargeReadable();2203});22042205test('writeFile (large file - readable) - unbuffered', async () => {2206setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);22072208return testWriteFileLargeReadable();2209});22102211async function testWriteFileLargeReadable() {2212const resource = URI.file(join(testDir, 'lorem.txt'));22132214const content = readFileSync(resource.fsPath);2215const newContent = content.toString() + content.toString();22162217const fileStat = await service.writeFile(resource, toLineByLineReadable(newContent));2218assert.strictEqual(fileStat.name, 'lorem.txt');22192220assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);2221}22222223test('writeFile (stream) - default', async () => {2224return testWriteFileStream();2225});22262227test('writeFile (stream) - buffered', async () => {2228setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);22292230return testWriteFileStream();2231});22322233test('writeFile (stream) - unbuffered', async () => {2234setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);22352236return testWriteFileStream();2237});22382239async function testWriteFileStream() {2240const source = URI.file(join(testDir, 'small.txt'));2241const target = URI.file(join(testDir, 'small-copy.txt'));22422243const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath)));2244assert.strictEqual(fileStat.name, 'small-copy.txt');22452246const targetContents = readFileSync(target.fsPath).toString();2247assert.strictEqual(readFileSync(source.fsPath).toString(), targetContents);2248}22492250test('writeFile (large file - stream) - default', async () => {2251return testWriteFileLargeStream();2252});22532254test('writeFile (large file - stream) - buffered', async () => {2255setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);22562257return testWriteFileLargeStream();2258});22592260test('writeFile (large file - stream) - unbuffered', async () => {2261setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);22622263return testWriteFileLargeStream();2264});22652266async function testWriteFileLargeStream() {2267const source = URI.file(join(testDir, 'lorem.txt'));2268const target = URI.file(join(testDir, 'lorem-copy.txt'));22692270const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath)));2271assert.strictEqual(fileStat.name, 'lorem-copy.txt');22722273const targetContents = readFileSync(target.fsPath).toString();2274assert.strictEqual(readFileSync(source.fsPath).toString(), targetContents);2275}22762277test('writeFile (file is created including parents)', async () => {2278const resource = URI.file(join(testDir, 'other', 'newfile.txt'));22792280const content = 'File is created including parent';2281const fileStat = await service.writeFile(resource, VSBuffer.fromString(content));2282assert.strictEqual(fileStat.name, 'newfile.txt');22832284assert.strictEqual(readFileSync(resource.fsPath).toString(), content);2285});22862287test('writeFile - locked files and unlocking', async () => {2288setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileWriteUnlock);22892290return testLockedFiles(false);2291});22922293test('writeFile (stream) - locked files and unlocking', async () => {2294setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileWriteUnlock);22952296return testLockedFiles(false);2297});22982299test('writeFile - locked files and unlocking throws error when missing capability', async () => {2300setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);23012302return testLockedFiles(true);2303});23042305test('writeFile (stream) - locked files and unlocking throws error when missing capability', async () => {2306setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);23072308return testLockedFiles(true);2309});23102311async function testLockedFiles(expectError: boolean) {2312const lockedFile = URI.file(join(testDir, 'my-locked-file'));23132314const content = await service.writeFile(lockedFile, VSBuffer.fromString('Locked File'));2315assert.strictEqual(content.locked, false);23162317const stats = await promises.stat(lockedFile.fsPath);2318await promises.chmod(lockedFile.fsPath, stats.mode & ~0o200);23192320let stat = await service.stat(lockedFile);2321assert.strictEqual(stat.locked, true);23222323let error;2324const newContent = 'Updates to locked file';2325try {2326await service.writeFile(lockedFile, VSBuffer.fromString(newContent));2327} catch (e) {2328error = e;2329}23302331assert.ok(error);2332error = undefined;23332334if (expectError) {2335try {2336await service.writeFile(lockedFile, VSBuffer.fromString(newContent), { unlock: true });2337} catch (e) {2338error = e;2339}23402341assert.ok(error);2342} else {2343await service.writeFile(lockedFile, VSBuffer.fromString(newContent), { unlock: true });2344assert.strictEqual(readFileSync(lockedFile.fsPath).toString(), newContent);23452346stat = await service.stat(lockedFile);2347assert.strictEqual(stat.locked, false);2348}2349}23502351test('writeFile (error when folder is encountered)', async () => {2352const resource = URI.file(testDir);23532354let error: Error | undefined = undefined;2355try {2356await service.writeFile(resource, VSBuffer.fromString('File is created including parent'));2357} catch (err) {2358error = err;2359}23602361assert.ok(error);2362});23632364test('writeFile (no error when providing up to date etag)', async () => {2365const resource = URI.file(join(testDir, 'small.txt'));23662367const stat = await service.resolve(resource);23682369const content = readFileSync(resource.fsPath).toString();2370assert.strictEqual(content, 'Small File');23712372const newContent = 'Updates to the small file';2373await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });23742375assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);2376});23772378test('writeFile - error when writing to file that has been updated meanwhile', async () => {2379const resource = URI.file(join(testDir, 'small.txt'));23802381const stat = await service.resolve(resource);23822383const content = readFileSync(resource.fsPath).toString();2384assert.strictEqual(content, 'Small File');23852386const newContent = 'Updates to the small file';2387await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });23882389const newContentLeadingToError = newContent + newContent;23902391const fakeMtime = 1000;2392const fakeSize = 1000;23932394let error: FileOperationError | undefined = undefined;2395try {2396await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToError), { etag: etag({ mtime: fakeMtime, size: fakeSize }), mtime: fakeMtime });2397} catch (err) {2398error = err;2399}24002401assert.ok(error);2402assert.ok(error instanceof FileOperationError);2403assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);2404});24052406test('writeFile - no error when writing to file where size is the same', async () => {2407const resource = URI.file(join(testDir, 'small.txt'));24082409const stat = await service.resolve(resource);24102411const content = readFileSync(resource.fsPath).toString();2412assert.strictEqual(content, 'Small File');24132414const newContent = content; // same content2415await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });24162417const newContentLeadingToNoError = newContent; // writing the same content should be OK24182419const fakeMtime = 1000;2420const actualSize = newContent.length;24212422let error: FileOperationError | undefined = undefined;2423try {2424await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToNoError), { etag: etag({ mtime: fakeMtime, size: actualSize }), mtime: fakeMtime });2425} catch (err) {2426error = err;2427}24282429assert.ok(!error);2430});24312432test('writeFile - no error when writing to file where content is the same', async () => {2433const resource = URI.file(join(testDir, 'small.txt'));24342435await service.resolve(resource);24362437const content = readFileSync(resource.fsPath).toString();2438assert.strictEqual(content, 'Small File');24392440const newContent = content; // same content2441let error: FileOperationError | undefined = undefined;2442try {2443await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: 'anything', mtime: 0 } /* fake it */);2444} catch (err) {2445error = err;2446}24472448assert.ok(!error);2449});24502451test('writeFile - error when writing to file where content is the same length but different', async () => {2452const resource = URI.file(join(testDir, 'small.txt'));24532454await service.resolve(resource);24552456const content = readFileSync(resource.fsPath).toString();2457assert.strictEqual(content, 'Small File');24582459const newContent = content.split('').reverse().join(''); // reverse content2460let error: FileOperationError | undefined = undefined;2461try {2462await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: 'anything', mtime: 0 } /* fake it */);2463} catch (err) {2464error = err;2465}24662467assert.ok(error);2468assert.ok(error instanceof FileOperationError);2469assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);2470});24712472test('writeFile - no error when writing to same nonexistent folder multiple times different new files', async () => {2473const newFolder = URI.file(join(testDir, 'some', 'new', 'folder'));24742475const file1 = joinPath(newFolder, 'file-1');2476const file2 = joinPath(newFolder, 'file-2');2477const file3 = joinPath(newFolder, 'file-3');24782479// this essentially verifies that the mkdirp logic implemented2480// in the file service is able to receive multiple requests for2481// the same folder and will not throw errors if another racing2482// call succeeded first.2483const newContent = 'Updates to the small file';2484await Promise.all([2485service.writeFile(file1, VSBuffer.fromString(newContent)),2486service.writeFile(file2, VSBuffer.fromString(newContent)),2487service.writeFile(file3, VSBuffer.fromString(newContent))2488]);24892490assert.ok(service.exists(file1));2491assert.ok(service.exists(file2));2492assert.ok(service.exists(file3));2493});24942495test('writeFile - error when writing to folder that is a file', async () => {2496const existingFile = URI.file(join(testDir, 'my-file'));24972498await service.createFile(existingFile);24992500const newFile = joinPath(existingFile, 'file-1');25012502let error;2503const newContent = 'Updates to the small file';2504try {2505await service.writeFile(newFile, VSBuffer.fromString(newContent));2506} catch (e) {2507error = e;2508}25092510assert.ok(error);2511});25122513test('appendFile', async () => {2514setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);25152516return testAppendFile();2517});25182519test('appendFile - buffered', async () => {2520setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);25212522return testAppendFile();2523});25242525async function testAppendFile() {2526let event: FileOperationEvent;2527disposables.add(service.onDidRunOperation(e => event = e));25282529const resource = URI.file(join(testDir, 'small.txt'));25302531const content = readFileSync(resource.fsPath).toString();2532assert.strictEqual(content, 'Small File');25332534const appendContent = ' - Appended!';2535await service.writeFile(resource, VSBuffer.fromString(appendContent), { append: true });25362537assert.ok(event!);2538assert.strictEqual(event!.resource.fsPath, resource.fsPath);2539assert.strictEqual(event!.operation, FileOperation.WRITE);25402541assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended!');2542}25432544test('appendFile (readable)', async () => {2545setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);25462547return testAppendFileReadable();2548});25492550test('appendFile (readable) - buffered', async () => {2551setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);25522553return testAppendFileReadable();2554});25552556async function testAppendFileReadable() {2557const resource = URI.file(join(testDir, 'small.txt'));25582559const content = readFileSync(resource.fsPath).toString();2560assert.strictEqual(content, 'Small File');25612562const appendContent = ' - Appended via readable!';2563await service.writeFile(resource, bufferToReadable(VSBuffer.fromString(appendContent)), { append: true });25642565assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended via readable!');2566}25672568test('appendFile (stream)', async () => {2569setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);25702571return testAppendFileStream();2572});25732574test('appendFile (stream) - buffered', async () => {2575setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);25762577return testAppendFileStream();2578});25792580async function testAppendFileStream() {2581const resource = URI.file(join(testDir, 'small.txt'));25822583const content = readFileSync(resource.fsPath).toString();2584assert.strictEqual(content, 'Small File');25852586const appendContent = ' - Appended via stream!';2587await service.writeFile(resource, bufferToStream(VSBuffer.fromString(appendContent)), { append: true });25882589assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended via stream!');2590}25912592test('appendFile - creates file if not exists', async () => {2593setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);25942595return testAppendFileCreatesFile();2596});25972598test('appendFile - creates file if not exists (buffered)', async () => {2599setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);26002601return testAppendFileCreatesFile();2602});26032604async function testAppendFileCreatesFile() {2605const resource = URI.file(join(testDir, 'appendfile-new.txt'));26062607assert.strictEqual(existsSync(resource.fsPath), false);26082609const content = 'Initial content via append';2610await service.writeFile(resource, VSBuffer.fromString(content), { append: true });26112612assert.strictEqual(existsSync(resource.fsPath), true);2613assert.strictEqual(readFileSync(resource.fsPath).toString(), content);2614}26152616test('appendFile - multiple appends', async () => {2617setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);26182619return testAppendFileMultiple();2620});26212622test('appendFile - multiple appends (buffered)', async () => {2623setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);26242625return testAppendFileMultiple();2626});26272628async function testAppendFileMultiple() {2629const resource = URI.file(join(testDir, 'appendfile-multiple.txt'));26302631await service.writeFile(resource, VSBuffer.fromString('Line 1\n'), { append: true });2632await service.writeFile(resource, VSBuffer.fromString('Line 2\n'), { append: true });2633await service.writeFile(resource, VSBuffer.fromString('Line 3\n'), { append: true });26342635assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Line 1\nLine 2\nLine 3\n');2636}26372638test('appendFile - throws when provider does not support append', async () => {2639// Remove FileAppend capability - should throw error2640setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);26412642const resource = URI.file(join(testDir, 'small.txt'));2643const appendContent = ' - Appended via fallback!';26442645let error: Error | undefined;2646try {2647await service.writeFile(resource, VSBuffer.fromString(appendContent), { append: true });2648} catch (e) {2649error = e as Error;2650}26512652assert.ok(error);2653assert.ok(error.message.includes('does not support append'));2654});26552656test('read - mixed positions', async () => {2657const resource = URI.file(join(testDir, 'lorem.txt'));26582659// read multiple times from position 02660let buffer = VSBuffer.alloc(1024);2661let fd = await fileProvider.open(resource, { create: false });2662for (let i = 0; i < 3; i++) {2663await fileProvider.read(fd, 0, buffer.buffer, 0, 26);2664assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');2665}2666await fileProvider.close(fd);26672668// read multiple times at various locations2669buffer = VSBuffer.alloc(1024);2670fd = await fileProvider.open(resource, { create: false });26712672let posInFile = 0;26732674await fileProvider.read(fd, posInFile, buffer.buffer, 0, 26);2675assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');2676posInFile += 26;26772678await fileProvider.read(fd, posInFile, buffer.buffer, 0, 1);2679assert.strictEqual(buffer.slice(0, 1).toString(), ',');2680posInFile += 1;26812682await fileProvider.read(fd, posInFile, buffer.buffer, 0, 12);2683assert.strictEqual(buffer.slice(0, 12).toString(), ' consectetur');2684posInFile += 12;26852686await fileProvider.read(fd, 98 /* no longer in sequence of posInFile */, buffer.buffer, 0, 9);2687assert.strictEqual(buffer.slice(0, 9).toString(), 'fermentum');26882689await fileProvider.read(fd, 27, buffer.buffer, 0, 12);2690assert.strictEqual(buffer.slice(0, 12).toString(), ' consectetur');26912692await fileProvider.read(fd, 26, buffer.buffer, 0, 1);2693assert.strictEqual(buffer.slice(0, 1).toString(), ',');26942695await fileProvider.read(fd, 0, buffer.buffer, 0, 26);2696assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');26972698await fileProvider.read(fd, posInFile /* back in sequence */, buffer.buffer, 0, 11);2699assert.strictEqual(buffer.slice(0, 11).toString(), ' adipiscing');27002701await fileProvider.close(fd);2702});27032704test('write - mixed positions', async () => {2705const resource = URI.file(join(testDir, 'lorem.txt'));27062707const buffer = VSBuffer.alloc(1024);2708const fdWrite = await fileProvider.open(resource, { create: true, unlock: false });2709const fdRead = await fileProvider.open(resource, { create: false });27102711let posInFileWrite = 0;2712let posInFileRead = 0;27132714const initialContents = VSBuffer.fromString('Lorem ipsum dolor sit amet');2715await fileProvider.write(fdWrite, posInFileWrite, initialContents.buffer, 0, initialContents.byteLength);2716posInFileWrite += initialContents.byteLength;27172718await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, 26);2719assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');2720posInFileRead += 26;27212722const contents = VSBuffer.fromString('Hello World');27232724await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);2725posInFileWrite += contents.byteLength;27262727await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, contents.byteLength);2728assert.strictEqual(buffer.slice(0, contents.byteLength).toString(), 'Hello World');2729posInFileRead += contents.byteLength;27302731await fileProvider.write(fdWrite, 6, contents.buffer, 0, contents.byteLength);27322733await fileProvider.read(fdRead, 0, buffer.buffer, 0, 11);2734assert.strictEqual(buffer.slice(0, 11).toString(), 'Lorem Hello');27352736await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);2737posInFileWrite += contents.byteLength;27382739await fileProvider.read(fdRead, posInFileWrite - contents.byteLength, buffer.buffer, 0, contents.byteLength);2740assert.strictEqual(buffer.slice(0, contents.byteLength).toString(), 'Hello World');27412742await fileProvider.close(fdWrite);2743await fileProvider.close(fdRead);2744});27452746test('readonly - is handled properly for a single resource', async () => {2747fileProvider.setReadonly(true);27482749const resource = URI.file(join(testDir, 'index.html'));27502751const resolveResult = await service.resolve(resource);2752assert.strictEqual(resolveResult.readonly, true);27532754const readResult = await service.readFile(resource);2755assert.strictEqual(readResult.readonly, true);27562757let writeFileError: Error | undefined = undefined;2758try {2759await service.writeFile(resource, VSBuffer.fromString('Hello Test'));2760} catch (error) {2761writeFileError = error;2762}2763assert.ok(writeFileError);27642765let deleteFileError: Error | undefined = undefined;2766try {2767await service.del(resource);2768} catch (error) {2769deleteFileError = error;2770}2771assert.ok(deleteFileError);2772});2773});277427752776