Path: blob/main/src/vs/platform/files/test/node/diskFileService.integrationTest.ts
3296 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.FileRealpath;7677if (isLinux) {78this._testCapabilities |= FileSystemProviderCapabilities.PathCaseSensitive;79}80}8182return this._testCapabilities;83}8485override set capabilities(capabilities: FileSystemProviderCapabilities) {86this._testCapabilities = capabilities;87}8889setInvalidStatSize(enabled: boolean): void {90this.invalidStatSize = enabled;91}9293setSmallStatSize(enabled: boolean): void {94this.smallStatSize = enabled;95}9697setReadonly(readonly: boolean): void {98this.readonly = readonly;99}100101override async stat(resource: URI): Promise<IStat> {102const res = await super.stat(resource);103104if (this.invalidStatSize) {105(res as any).size = String(res.size) as any; // for https://github.com/microsoft/vscode/issues/72909106} else if (this.smallStatSize) {107(res as any).size = 1;108} else if (this.readonly) {109(res as any).permissions = FilePermission.Readonly;110}111112return res;113}114115override async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {116const bytesRead = await super.read(fd, pos, data, offset, length);117118this.totalBytesRead += bytesRead;119120return bytesRead;121}122123override async readFile(resource: URI, options?: IFileAtomicReadOptions): Promise<Uint8Array> {124const res = await super.readFile(resource, options);125126this.totalBytesRead += res.byteLength;127128return res;129}130}131132DiskFileSystemProvider.configureFlushOnWrite(false); // speed up all unit tests by disabling flush on write133134flakySuite('Disk File Service', function () {135136const testSchema = 'test';137138let service: FileService;139let fileProvider: TestDiskFileSystemProvider;140let testProvider: TestDiskFileSystemProvider;141142let testDir: string;143144const disposables = new DisposableStore();145146setup(async () => {147const logService = new NullLogService();148149service = disposables.add(new FileService(logService));150151fileProvider = disposables.add(new TestDiskFileSystemProvider(logService));152disposables.add(service.registerProvider(Schemas.file, fileProvider));153154testProvider = disposables.add(new TestDiskFileSystemProvider(logService));155disposables.add(service.registerProvider(testSchema, testProvider));156157testDir = getRandomTestPath(tmpdir(), 'vsctests', 'diskfileservice');158159const sourceDir = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/service').fsPath;160161await Promises.copy(sourceDir, testDir, { preserveSymlinks: false });162});163164teardown(() => {165disposables.clear();166167return Promises.rm(testDir);168});169170test('createFolder', async () => {171let event: FileOperationEvent | undefined;172disposables.add(service.onDidRunOperation(e => event = e));173174const parent = await service.resolve(URI.file(testDir));175176const newFolderResource = URI.file(join(parent.resource.fsPath, 'newFolder'));177178const newFolder = await service.createFolder(newFolderResource);179180assert.strictEqual(newFolder.name, 'newFolder');181assert.strictEqual(existsSync(newFolder.resource.fsPath), true);182183assert.ok(event);184assert.strictEqual(event.resource.fsPath, newFolderResource.fsPath);185assert.strictEqual(event.operation, FileOperation.CREATE);186assert.strictEqual(event.target!.resource.fsPath, newFolderResource.fsPath);187assert.strictEqual(event.target!.isDirectory, true);188});189190test('createFolder: creating multiple folders at once', async () => {191let event: FileOperationEvent;192disposables.add(service.onDidRunOperation(e => event = e));193194const multiFolderPaths = ['a', 'couple', 'of', 'folders'];195const parent = await service.resolve(URI.file(testDir));196197const newFolderResource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths));198199const newFolder = await service.createFolder(newFolderResource);200201const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1];202assert.strictEqual(newFolder.name, lastFolderName);203assert.strictEqual(existsSync(newFolder.resource.fsPath), true);204205assert.ok(event!);206assert.strictEqual(event!.resource.fsPath, newFolderResource.fsPath);207assert.strictEqual(event!.operation, FileOperation.CREATE);208assert.strictEqual(event!.target!.resource.fsPath, newFolderResource.fsPath);209assert.strictEqual(event!.target!.isDirectory, true);210});211212test('exists', async () => {213let exists = await service.exists(URI.file(testDir));214assert.strictEqual(exists, true);215216exists = await service.exists(URI.file(testDir + 'something'));217assert.strictEqual(exists, false);218});219220test('resolve - file', async () => {221const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/index.html');222const resolved = await service.resolve(resource);223224assert.strictEqual(resolved.name, 'index.html');225assert.strictEqual(resolved.isFile, true);226assert.strictEqual(resolved.isDirectory, false);227assert.strictEqual(resolved.readonly, false);228assert.strictEqual(resolved.isSymbolicLink, false);229assert.strictEqual(resolved.resource.toString(), resource.toString());230assert.strictEqual(resolved.children, undefined);231assert.ok(resolved.mtime! > 0);232assert.ok(resolved.ctime! > 0);233assert.ok(resolved.size! > 0);234});235236test('resolve - directory', async () => {237const testsElements = ['examples', 'other', 'index.html', 'site.css'];238239const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver');240const result = await service.resolve(resource);241242assert.ok(result);243assert.strictEqual(result.resource.toString(), resource.toString());244assert.strictEqual(result.name, 'resolver');245assert.ok(result.children);246assert.ok(result.children.length > 0);247assert.ok(result.isDirectory);248assert.strictEqual(result.readonly, false);249assert.ok(result.mtime! > 0);250assert.ok(result.ctime! > 0);251assert.strictEqual(result.children.length, testsElements.length);252253assert.ok(result.children.every(entry => {254return testsElements.some(name => {255return basename(entry.resource.fsPath) === name;256});257}));258259result.children.forEach(value => {260assert.ok(basename(value.resource.fsPath));261if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {262assert.ok(value.isDirectory);263assert.strictEqual(value.mtime, undefined);264assert.strictEqual(value.ctime, undefined);265} else if (basename(value.resource.fsPath) === 'index.html') {266assert.ok(!value.isDirectory);267assert.ok(!value.children);268assert.strictEqual(value.mtime, undefined);269assert.strictEqual(value.ctime, undefined);270} else if (basename(value.resource.fsPath) === 'site.css') {271assert.ok(!value.isDirectory);272assert.ok(!value.children);273assert.strictEqual(value.mtime, undefined);274assert.strictEqual(value.ctime, undefined);275} else {276assert.fail('Unexpected value ' + basename(value.resource.fsPath));277}278});279});280281test('resolve - directory - with metadata', async () => {282const testsElements = ['examples', 'other', 'index.html', 'site.css'];283284const result = await service.resolve(FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver'), { resolveMetadata: true });285286assert.ok(result);287assert.strictEqual(result.name, 'resolver');288assert.ok(result.children);289assert.ok(result.children.length > 0);290assert.ok(result.isDirectory);291assert.ok(result.mtime > 0);292assert.ok(result.ctime > 0);293assert.strictEqual(result.children.length, testsElements.length);294295assert.ok(result.children.every(entry => {296return testsElements.some(name => {297return basename(entry.resource.fsPath) === name;298});299}));300301assert.ok(result.children.every(entry => entry.etag.length > 0));302303result.children.forEach(value => {304assert.ok(basename(value.resource.fsPath));305if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) {306assert.ok(value.isDirectory);307assert.ok(value.mtime > 0);308assert.ok(value.ctime > 0);309} else if (basename(value.resource.fsPath) === 'index.html') {310assert.ok(!value.isDirectory);311assert.ok(!value.children);312assert.ok(value.mtime > 0);313assert.ok(value.ctime > 0);314} else if (basename(value.resource.fsPath) === 'site.css') {315assert.ok(!value.isDirectory);316assert.ok(!value.children);317assert.ok(value.mtime > 0);318assert.ok(value.ctime > 0);319} else {320assert.fail('Unexpected value ' + basename(value.resource.fsPath));321}322});323});324325test('resolve - directory with resolveTo', async () => {326const resolved = await service.resolve(URI.file(testDir), { resolveTo: [URI.file(join(testDir, 'deep'))] });327assert.strictEqual(resolved.children!.length, 8);328329const deep = (getByName(resolved, 'deep')!);330assert.strictEqual(deep.children!.length, 4);331});332333test('resolve - directory - resolveTo single directory', async () => {334const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver').fsPath;335const result = await service.resolve(URI.file(resolverFixturesPath), { resolveTo: [URI.file(join(resolverFixturesPath, 'other/deep'))] });336337assert.ok(result);338assert.ok(result.children);339assert.ok(result.children.length > 0);340assert.ok(result.isDirectory);341342const children = result.children;343assert.strictEqual(children.length, 4);344345const other = getByName(result, 'other');346assert.ok(other);347assert.ok(other.children!.length > 0);348349const deep = getByName(other, 'deep');350assert.ok(deep);351assert.ok(deep.children!.length > 0);352assert.strictEqual(deep.children!.length, 4);353});354355test('resolve directory - resolveTo multiple directories', () => {356return testResolveDirectoryWithTarget(false);357});358359test('resolve directory - resolveTo with a URI that has query parameter (https://github.com/microsoft/vscode/issues/128151)', () => {360return testResolveDirectoryWithTarget(true);361});362363async function testResolveDirectoryWithTarget(withQueryParam: boolean): Promise<void> {364const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver').fsPath;365const result = await service.resolve(URI.file(resolverFixturesPath).with({ query: withQueryParam ? 'test' : undefined }), {366resolveTo: [367URI.file(join(resolverFixturesPath, 'other/deep')).with({ query: withQueryParam ? 'test' : undefined }),368URI.file(join(resolverFixturesPath, 'examples')).with({ query: withQueryParam ? 'test' : undefined })369]370});371372assert.ok(result);373assert.ok(result.children);374assert.ok(result.children.length > 0);375assert.ok(result.isDirectory);376377const children = result.children;378assert.strictEqual(children.length, 4);379380const other = getByName(result, 'other');381assert.ok(other);382assert.ok(other.children!.length > 0);383384const deep = getByName(other, 'deep');385assert.ok(deep);386assert.ok(deep.children!.length > 0);387assert.strictEqual(deep.children!.length, 4);388389const examples = getByName(result, 'examples');390assert.ok(examples);391assert.ok(examples.children!.length > 0);392assert.strictEqual(examples.children!.length, 4);393}394395test('resolve directory - resolveSingleChildFolders', async () => {396const resolverFixturesPath = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/other').fsPath;397const result = await service.resolve(URI.file(resolverFixturesPath), { resolveSingleChildDescendants: true });398399assert.ok(result);400assert.ok(result.children);401assert.ok(result.children.length > 0);402assert.ok(result.isDirectory);403404const children = result.children;405assert.strictEqual(children.length, 1);406407const deep = getByName(result, 'deep');408assert.ok(deep);409assert.ok(deep.children!.length > 0);410assert.strictEqual(deep.children!.length, 4);411});412413test('resolves', async () => {414const res = await service.resolveAll([415{ resource: URI.file(testDir), options: { resolveTo: [URI.file(join(testDir, 'deep'))] } },416{ resource: URI.file(join(testDir, 'deep')) }417]);418419const r1 = (res[0].stat!);420assert.strictEqual(r1.children!.length, 8);421422const deep = (getByName(r1, 'deep')!);423assert.strictEqual(deep.children!.length, 4);424425const r2 = (res[1].stat!);426assert.strictEqual(r2.children!.length, 4);427assert.strictEqual(r2.name, 'deep');428});429430test('resolve / realpath - folder symbolic link', async () => {431const link = URI.file(join(testDir, 'deep-link'));432await promises.symlink(join(testDir, 'deep'), link.fsPath, 'junction');433434const resolved = await service.resolve(link);435assert.strictEqual(resolved.children!.length, 4);436assert.strictEqual(resolved.isDirectory, true);437assert.strictEqual(resolved.isSymbolicLink, true);438439const realpath = await service.realpath(link);440assert.ok(realpath);441assert.strictEqual(basename(realpath.fsPath), 'deep');442});443444(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('resolve - file symbolic link', async () => {445const link = URI.file(join(testDir, 'lorem.txt-linked'));446await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath);447448const resolved = await service.resolve(link);449assert.strictEqual(resolved.isDirectory, false);450assert.strictEqual(resolved.isSymbolicLink, true);451});452453test('resolve - symbolic link pointing to nonexistent file does not break', async () => {454await promises.symlink(join(testDir, 'foo'), join(testDir, 'bar'), 'junction');455456const resolved = await service.resolve(URI.file(testDir));457assert.strictEqual(resolved.isDirectory, true);458assert.strictEqual(resolved.children!.length, 9);459460const resolvedLink = resolved.children?.find(child => child.name === 'bar' && child.isSymbolicLink);461assert.ok(resolvedLink);462463assert.ok(!resolvedLink?.isDirectory);464assert.ok(!resolvedLink?.isFile);465});466467test('stat - file', async () => {468const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver/index.html');469const resolved = await service.stat(resource);470471assert.strictEqual(resolved.name, 'index.html');472assert.strictEqual(resolved.isFile, true);473assert.strictEqual(resolved.isDirectory, false);474assert.strictEqual(resolved.readonly, false);475assert.strictEqual(resolved.isSymbolicLink, false);476assert.strictEqual(resolved.resource.toString(), resource.toString());477assert.ok(resolved.mtime > 0);478assert.ok(resolved.ctime > 0);479assert.ok(resolved.size > 0);480});481482test('stat - directory', async () => {483const resource = FileAccess.asFileUri('vs/platform/files/test/node/fixtures/resolver');484const result = await service.stat(resource);485486assert.ok(result);487assert.strictEqual(result.resource.toString(), resource.toString());488assert.strictEqual(result.name, 'resolver');489assert.ok(result.isDirectory);490assert.strictEqual(result.readonly, false);491assert.ok(result.mtime > 0);492assert.ok(result.ctime > 0);493});494495test('deleteFile (non recursive)', async () => {496return testDeleteFile(false, false);497});498499test('deleteFile (recursive)', async () => {500return testDeleteFile(false, true);501});502503(isLinux /* trash is unreliable on Linux */ ? test.skip : test)('deleteFile (useTrash)', async () => {504return testDeleteFile(true, false);505});506507async function testDeleteFile(useTrash: boolean, recursive: boolean): Promise<void> {508let event: FileOperationEvent;509disposables.add(service.onDidRunOperation(e => event = e));510511const resource = URI.file(join(testDir, 'deep', 'conway.js'));512const source = await service.resolve(resource);513514assert.strictEqual(await service.canDelete(source.resource, { useTrash, recursive }), true);515await service.del(source.resource, { useTrash, recursive });516517assert.strictEqual(existsSync(source.resource.fsPath), false);518519assert.ok(event!);520assert.strictEqual(event!.resource.fsPath, resource.fsPath);521assert.strictEqual(event!.operation, FileOperation.DELETE);522523let error: Error | undefined = undefined;524try {525await service.del(source.resource, { useTrash, recursive });526} catch (e) {527error = e;528}529530assert.ok(error);531assert.strictEqual((<FileOperationError>error).fileOperationResult, FileOperationResult.FILE_NOT_FOUND);532}533534(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (exists)', async () => {535const target = URI.file(join(testDir, 'lorem.txt'));536const link = URI.file(join(testDir, 'lorem.txt-linked'));537await promises.symlink(target.fsPath, link.fsPath);538539const source = await service.resolve(link);540541let event: FileOperationEvent;542disposables.add(service.onDidRunOperation(e => event = e));543544assert.strictEqual(await service.canDelete(source.resource), true);545await service.del(source.resource);546547assert.strictEqual(existsSync(source.resource.fsPath), false);548549assert.ok(event!);550assert.strictEqual(event!.resource.fsPath, link.fsPath);551assert.strictEqual(event!.operation, FileOperation.DELETE);552553assert.strictEqual(existsSync(target.fsPath), true); // target the link pointed to is never deleted554});555556(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (pointing to nonexistent file)', async () => {557const target = URI.file(join(testDir, 'foo'));558const link = URI.file(join(testDir, 'bar'));559await promises.symlink(target.fsPath, link.fsPath);560561let event: FileOperationEvent;562disposables.add(service.onDidRunOperation(e => event = e));563564assert.strictEqual(await service.canDelete(link), true);565await service.del(link);566567assert.strictEqual(existsSync(link.fsPath), false);568569assert.ok(event!);570assert.strictEqual(event!.resource.fsPath, link.fsPath);571assert.strictEqual(event!.operation, FileOperation.DELETE);572});573574test('deleteFolder (recursive)', async () => {575return testDeleteFolderRecursive(false, false);576});577578test('deleteFolder (recursive, atomic)', async () => {579return testDeleteFolderRecursive(false, { postfix: '.vsctmp' });580});581582(isLinux /* trash is unreliable on Linux */ ? test.skip : test)('deleteFolder (recursive, useTrash)', async () => {583return testDeleteFolderRecursive(true, false);584});585586async function testDeleteFolderRecursive(useTrash: boolean, atomic: IFileAtomicOptions | false): Promise<void> {587let event: FileOperationEvent;588disposables.add(service.onDidRunOperation(e => event = e));589590const resource = URI.file(join(testDir, 'deep'));591const source = await service.resolve(resource);592593assert.strictEqual(await service.canDelete(source.resource, { recursive: true, useTrash, atomic }), true);594await service.del(source.resource, { recursive: true, useTrash, atomic });595596assert.strictEqual(existsSync(source.resource.fsPath), false);597assert.ok(event!);598assert.strictEqual(event!.resource.fsPath, resource.fsPath);599assert.strictEqual(event!.operation, FileOperation.DELETE);600}601602test('deleteFolder (non recursive)', async () => {603const resource = URI.file(join(testDir, 'deep'));604const source = await service.resolve(resource);605606assert.ok((await service.canDelete(source.resource)) instanceof Error);607608let error;609try {610await service.del(source.resource);611} catch (e) {612error = e;613}614615assert.ok(error);616});617618test('deleteFolder empty folder (recursive)', () => {619return testDeleteEmptyFolder(true);620});621622test('deleteFolder empty folder (non recursive)', () => {623return testDeleteEmptyFolder(false);624});625626async function testDeleteEmptyFolder(recursive: boolean): Promise<void> {627const { resource } = await service.createFolder(URI.file(join(testDir, 'deep', 'empty')));628629await service.del(resource, { recursive });630631assert.strictEqual(await service.exists(resource), false);632}633634test('move', async () => {635let event: FileOperationEvent;636disposables.add(service.onDidRunOperation(e => event = e));637638const source = URI.file(join(testDir, 'index.html'));639const sourceContents = readFileSync(source.fsPath);640641const target = URI.file(join(dirname(source.fsPath), 'other.html'));642643assert.strictEqual(await service.canMove(source, target), true);644const renamed = await service.move(source, target);645646assert.strictEqual(existsSync(renamed.resource.fsPath), true);647assert.strictEqual(existsSync(source.fsPath), false);648assert.ok(event!);649assert.strictEqual(event!.resource.fsPath, source.fsPath);650assert.strictEqual(event!.operation, FileOperation.MOVE);651assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);652653const targetContents = readFileSync(target.fsPath);654655assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);656assert.strictEqual(sourceContents.toString(), targetContents.toString());657});658659test('move - across providers (buffered => buffered)', async () => {660setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);661setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);662663return testMoveAcrossProviders();664});665666test('move - across providers (unbuffered => unbuffered)', async () => {667setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);668setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);669670return testMoveAcrossProviders();671});672673test('move - across providers (buffered => unbuffered)', async () => {674setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);675setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);676677return testMoveAcrossProviders();678});679680test('move - across providers (unbuffered => buffered)', async () => {681setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);682setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);683684return testMoveAcrossProviders();685});686687test('move - across providers - large (buffered => buffered)', async () => {688setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);689setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);690691return testMoveAcrossProviders('lorem.txt');692});693694test('move - across providers - large (unbuffered => unbuffered)', async () => {695setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);696setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);697698return testMoveAcrossProviders('lorem.txt');699});700701test('move - across providers - large (buffered => unbuffered)', async () => {702setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);703setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);704705return testMoveAcrossProviders('lorem.txt');706});707708test('move - across providers - large (unbuffered => buffered)', async () => {709setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);710setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);711712return testMoveAcrossProviders('lorem.txt');713});714715async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> {716let event: FileOperationEvent;717disposables.add(service.onDidRunOperation(e => event = e));718719const source = URI.file(join(testDir, sourceFile));720const sourceContents = readFileSync(source.fsPath);721722const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema });723724assert.strictEqual(await service.canMove(source, target), true);725const renamed = await service.move(source, target);726727assert.strictEqual(existsSync(renamed.resource.fsPath), true);728assert.strictEqual(existsSync(source.fsPath), false);729assert.ok(event!);730assert.strictEqual(event!.resource.fsPath, source.fsPath);731assert.strictEqual(event!.operation, FileOperation.COPY);732assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);733734const targetContents = readFileSync(target.fsPath);735736assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);737assert.strictEqual(sourceContents.toString(), targetContents.toString());738}739740test('move - multi folder', async () => {741let event: FileOperationEvent;742disposables.add(service.onDidRunOperation(e => event = e));743744const multiFolderPaths = ['a', 'couple', 'of', 'folders'];745const renameToPath = join(...multiFolderPaths, 'other.html');746747const source = URI.file(join(testDir, 'index.html'));748749assert.strictEqual(await service.canMove(source, URI.file(join(dirname(source.fsPath), renameToPath))), true);750const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), renameToPath)));751752assert.strictEqual(existsSync(renamed.resource.fsPath), true);753assert.strictEqual(existsSync(source.fsPath), false);754assert.ok(event!);755assert.strictEqual(event!.resource.fsPath, source.fsPath);756assert.strictEqual(event!.operation, FileOperation.MOVE);757assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);758});759760test('move - directory', async () => {761let event: FileOperationEvent;762disposables.add(service.onDidRunOperation(e => event = e));763764const source = URI.file(join(testDir, 'deep'));765766assert.strictEqual(await service.canMove(source, URI.file(join(dirname(source.fsPath), 'deeper'))), true);767const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'deeper')));768769assert.strictEqual(existsSync(renamed.resource.fsPath), true);770assert.strictEqual(existsSync(source.fsPath), false);771assert.ok(event!);772assert.strictEqual(event!.resource.fsPath, source.fsPath);773assert.strictEqual(event!.operation, FileOperation.MOVE);774assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);775});776777test('move - directory - across providers (buffered => buffered)', async () => {778setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);779setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);780781return testMoveFolderAcrossProviders();782});783784test('move - directory - across providers (unbuffered => unbuffered)', async () => {785setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);786setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);787788return testMoveFolderAcrossProviders();789});790791test('move - directory - across providers (buffered => unbuffered)', async () => {792setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);793setCapabilities(testProvider, FileSystemProviderCapabilities.FileReadWrite);794795return testMoveFolderAcrossProviders();796});797798test('move - directory - across providers (unbuffered => buffered)', async () => {799setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);800setCapabilities(testProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);801802return testMoveFolderAcrossProviders();803});804805async function testMoveFolderAcrossProviders(): Promise<void> {806let event: FileOperationEvent;807disposables.add(service.onDidRunOperation(e => event = e));808809const source = URI.file(join(testDir, 'deep'));810const sourceChildren = readdirSync(source.fsPath);811812const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema });813814assert.strictEqual(await service.canMove(source, target), true);815const renamed = await service.move(source, target);816817assert.strictEqual(existsSync(renamed.resource.fsPath), true);818assert.strictEqual(existsSync(source.fsPath), false);819assert.ok(event!);820assert.strictEqual(event!.resource.fsPath, source.fsPath);821assert.strictEqual(event!.operation, FileOperation.COPY);822assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);823824const targetChildren = readdirSync(target.fsPath);825assert.strictEqual(sourceChildren.length, targetChildren.length);826for (let i = 0; i < sourceChildren.length; i++) {827assert.strictEqual(sourceChildren[i], targetChildren[i]);828}829}830831test('move - MIX CASE', async () => {832let event: FileOperationEvent;833disposables.add(service.onDidRunOperation(e => event = e));834835const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });836assert.ok(source.size > 0);837838const renamedResource = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));839assert.strictEqual(await service.canMove(source.resource, renamedResource), true);840let renamed = await service.move(source.resource, renamedResource);841842assert.strictEqual(existsSync(renamedResource.fsPath), true);843assert.strictEqual(basename(renamedResource.fsPath), 'INDEX.html');844assert.ok(event!);845assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);846assert.strictEqual(event!.operation, FileOperation.MOVE);847assert.strictEqual(event!.target!.resource.fsPath, renamedResource.fsPath);848849renamed = await service.resolve(renamedResource, { resolveMetadata: true });850assert.strictEqual(source.size, renamed.size);851});852853test('move - same file', async () => {854let event: FileOperationEvent;855disposables.add(service.onDidRunOperation(e => event = e));856857const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });858assert.ok(source.size > 0);859860assert.strictEqual(await service.canMove(source.resource, URI.file(source.resource.fsPath)), true);861let renamed = await service.move(source.resource, URI.file(source.resource.fsPath));862863assert.strictEqual(existsSync(renamed.resource.fsPath), true);864assert.strictEqual(basename(renamed.resource.fsPath), 'index.html');865assert.ok(event!);866assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);867assert.strictEqual(event!.operation, FileOperation.MOVE);868assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);869870renamed = await service.resolve(renamed.resource, { resolveMetadata: true });871assert.strictEqual(source.size, renamed.size);872});873874test('move - same file #2', async () => {875let event: FileOperationEvent;876disposables.add(service.onDidRunOperation(e => event = e));877878const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });879assert.ok(source.size > 0);880881const targetParent = URI.file(testDir);882const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });883884assert.strictEqual(await service.canMove(source.resource, target), true);885let renamed = await service.move(source.resource, target);886887assert.strictEqual(existsSync(renamed.resource.fsPath), true);888assert.strictEqual(basename(renamed.resource.fsPath), 'index.html');889assert.ok(event!);890assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);891assert.strictEqual(event!.operation, FileOperation.MOVE);892assert.strictEqual(event!.target!.resource.fsPath, renamed.resource.fsPath);893894renamed = await service.resolve(renamed.resource, { resolveMetadata: true });895assert.strictEqual(source.size, renamed.size);896});897898test('move - source parent of target', async () => {899let event: FileOperationEvent;900disposables.add(service.onDidRunOperation(e => event = e));901902let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });903const originalSize = source.size;904assert.ok(originalSize > 0);905906assert.ok((await service.canMove(URI.file(testDir), URI.file(join(testDir, 'binary.txt'))) instanceof Error));907908let error;909try {910await service.move(URI.file(testDir), URI.file(join(testDir, 'binary.txt')));911} catch (e) {912error = e;913}914915assert.ok(error);916assert.ok(!event!);917918source = await service.resolve(source.resource, { resolveMetadata: true });919assert.strictEqual(originalSize, source.size);920});921922test('move - FILE_MOVE_CONFLICT', async () => {923let event: FileOperationEvent;924disposables.add(service.onDidRunOperation(e => event = e));925926let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });927const originalSize = source.size;928assert.ok(originalSize > 0);929930assert.ok((await service.canMove(source.resource, URI.file(join(testDir, 'binary.txt'))) instanceof Error));931932let error;933try {934await service.move(source.resource, URI.file(join(testDir, 'binary.txt')));935} catch (e) {936error = e;937}938939assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT);940assert.ok(!event!);941942source = await service.resolve(source.resource, { resolveMetadata: true });943assert.strictEqual(originalSize, source.size);944});945946test('move - overwrite folder with file', async () => {947let createEvent: FileOperationEvent;948let moveEvent: FileOperationEvent;949let deleteEvent: FileOperationEvent;950disposables.add(service.onDidRunOperation(e => {951if (e.operation === FileOperation.CREATE) {952createEvent = e;953} else if (e.operation === FileOperation.DELETE) {954deleteEvent = e;955} else if (e.operation === FileOperation.MOVE) {956moveEvent = e;957}958}));959960const parent = await service.resolve(URI.file(testDir));961const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));962const f = await service.createFolder(folderResource);963const source = URI.file(join(testDir, 'deep', 'conway.js'));964965assert.strictEqual(await service.canMove(source, f.resource, true), true);966const moved = await service.move(source, f.resource, true);967968assert.strictEqual(existsSync(moved.resource.fsPath), true);969assert.ok(statSync(moved.resource.fsPath).isFile);970assert.ok(createEvent!);971assert.ok(deleteEvent!);972assert.ok(moveEvent!);973assert.strictEqual(moveEvent!.resource.fsPath, source.fsPath);974assert.strictEqual(moveEvent!.target!.resource.fsPath, moved.resource.fsPath);975assert.strictEqual(deleteEvent!.resource.fsPath, folderResource.fsPath);976});977978test('copy', async () => {979await doTestCopy();980});981982test('copy - unbuffered (FileSystemProviderCapabilities.FileReadWrite)', async () => {983setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);984985await doTestCopy();986});987988test('copy - unbuffered large (FileSystemProviderCapabilities.FileReadWrite)', async () => {989setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);990991await doTestCopy('lorem.txt');992});993994test('copy - buffered (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {995setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);996997await doTestCopy();998});9991000test('copy - buffered large (FileSystemProviderCapabilities.FileOpenReadWriteClose)', async () => {1001setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);10021003await doTestCopy('lorem.txt');1004});10051006function setCapabilities(provider: TestDiskFileSystemProvider, capabilities: FileSystemProviderCapabilities): void {1007provider.capabilities = capabilities;1008if (isLinux) {1009provider.capabilities |= FileSystemProviderCapabilities.PathCaseSensitive;1010}1011}10121013async function doTestCopy(sourceName: string = 'index.html') {1014let event: FileOperationEvent;1015disposables.add(service.onDidRunOperation(e => event = e));10161017const source = await service.resolve(URI.file(join(testDir, sourceName)));1018const target = URI.file(join(testDir, 'other.html'));10191020assert.strictEqual(await service.canCopy(source.resource, target), true);1021const copied = await service.copy(source.resource, target);10221023assert.strictEqual(existsSync(copied.resource.fsPath), true);1024assert.strictEqual(existsSync(source.resource.fsPath), true);1025assert.ok(event!);1026assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);1027assert.strictEqual(event!.operation, FileOperation.COPY);1028assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);10291030const sourceContents = readFileSync(source.resource.fsPath);1031const targetContents = readFileSync(target.fsPath);10321033assert.strictEqual(sourceContents.byteLength, targetContents.byteLength);1034assert.strictEqual(sourceContents.toString(), targetContents.toString());1035}10361037test('copy - overwrite folder with file', async () => {1038let createEvent: FileOperationEvent;1039let copyEvent: FileOperationEvent;1040let deleteEvent: FileOperationEvent;1041disposables.add(service.onDidRunOperation(e => {1042if (e.operation === FileOperation.CREATE) {1043createEvent = e;1044} else if (e.operation === FileOperation.DELETE) {1045deleteEvent = e;1046} else if (e.operation === FileOperation.COPY) {1047copyEvent = e;1048}1049}));10501051const parent = await service.resolve(URI.file(testDir));1052const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js'));1053const f = await service.createFolder(folderResource);1054const source = URI.file(join(testDir, 'deep', 'conway.js'));10551056assert.strictEqual(await service.canCopy(source, f.resource, true), true);1057const copied = await service.copy(source, f.resource, true);10581059assert.strictEqual(existsSync(copied.resource.fsPath), true);1060assert.ok(statSync(copied.resource.fsPath).isFile);1061assert.ok(createEvent!);1062assert.ok(deleteEvent!);1063assert.ok(copyEvent!);1064assert.strictEqual(copyEvent!.resource.fsPath, source.fsPath);1065assert.strictEqual(copyEvent!.target!.resource.fsPath, copied.resource.fsPath);1066assert.strictEqual(deleteEvent!.resource.fsPath, folderResource.fsPath);1067});10681069test('copy - MIX CASE same target - no overwrite', async () => {1070let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1071const originalSize = source.size;1072assert.ok(originalSize > 0);10731074const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));10751076const canCopy = await service.canCopy(source.resource, target);10771078let error;1079let copied: IFileStatWithMetadata;1080try {1081copied = await service.copy(source.resource, target);1082} catch (e) {1083error = e;1084}10851086if (isLinux) {1087assert.ok(!error);1088assert.strictEqual(canCopy, true);10891090assert.strictEqual(existsSync(copied!.resource.fsPath), true);1091assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));1092assert.strictEqual(source.size, copied!.size);1093} else {1094assert.ok(error);1095assert.ok(canCopy instanceof Error);10961097source = await service.resolve(source.resource, { resolveMetadata: true });1098assert.strictEqual(originalSize, source.size);1099}1100});11011102test('copy - MIX CASE same target - overwrite', async () => {1103let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1104const originalSize = source.size;1105assert.ok(originalSize > 0);11061107const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'));11081109const canCopy = await service.canCopy(source.resource, target, true);11101111let error;1112let copied: IFileStatWithMetadata;1113try {1114copied = await service.copy(source.resource, target, true);1115} catch (e) {1116error = e;1117}11181119if (isLinux) {1120assert.ok(!error);1121assert.strictEqual(canCopy, true);11221123assert.strictEqual(existsSync(copied!.resource.fsPath), true);1124assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html'));1125assert.strictEqual(source.size, copied!.size);1126} else {1127assert.ok(error);1128assert.ok(canCopy instanceof Error);11291130source = await service.resolve(source.resource, { resolveMetadata: true });1131assert.strictEqual(originalSize, source.size);1132}1133});11341135test('copy - MIX CASE different target - overwrite', async () => {1136const source1 = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1137assert.ok(source1.size > 0);11381139const renamed = await service.move(source1.resource, URI.file(join(dirname(source1.resource.fsPath), 'CONWAY.js')));1140assert.strictEqual(existsSync(renamed.resource.fsPath), true);1141assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js'));1142assert.strictEqual(source1.size, renamed.size);11431144const source2 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')), { resolveMetadata: true });1145const target = URI.file(join(testDir, basename(source2.resource.path)));11461147assert.strictEqual(await service.canCopy(source2.resource, target, true), true);1148const res = await service.copy(source2.resource, target, true);1149assert.strictEqual(existsSync(res.resource.fsPath), true);1150assert.ok(readdirSync(testDir).some(f => f === 'conway.js'));1151assert.strictEqual(source2.size, res.size);1152});11531154test('copy - same file', async () => {1155let event: FileOperationEvent;1156disposables.add(service.onDidRunOperation(e => event = e));11571158const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1159assert.ok(source.size > 0);11601161assert.strictEqual(await service.canCopy(source.resource, URI.file(source.resource.fsPath)), true);1162let copied = await service.copy(source.resource, URI.file(source.resource.fsPath));11631164assert.strictEqual(existsSync(copied.resource.fsPath), true);1165assert.strictEqual(basename(copied.resource.fsPath), 'index.html');1166assert.ok(event!);1167assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);1168assert.strictEqual(event!.operation, FileOperation.COPY);1169assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);11701171copied = await service.resolve(source.resource, { resolveMetadata: true });1172assert.strictEqual(source.size, copied.size);1173});11741175test('copy - same file #2', async () => {1176let event: FileOperationEvent;1177disposables.add(service.onDidRunOperation(e => event = e));11781179const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });1180assert.ok(source.size > 0);11811182const targetParent = URI.file(testDir);1183const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) });11841185assert.strictEqual(await service.canCopy(source.resource, URI.file(target.fsPath)), true);1186let copied = await service.copy(source.resource, URI.file(target.fsPath));11871188assert.strictEqual(existsSync(copied.resource.fsPath), true);1189assert.strictEqual(basename(copied.resource.fsPath), 'index.html');1190assert.ok(event!);1191assert.strictEqual(event!.resource.fsPath, source.resource.fsPath);1192assert.strictEqual(event!.operation, FileOperation.COPY);1193assert.strictEqual(event!.target!.resource.fsPath, copied.resource.fsPath);11941195copied = await service.resolve(source.resource, { resolveMetadata: true });1196assert.strictEqual(source.size, copied.size);1197});11981199test('cloneFile - basics', () => {1200return testCloneFile();1201});12021203test('cloneFile - via copy capability', () => {1204setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileFolderCopy);12051206return testCloneFile();1207});12081209test('cloneFile - via pipe', () => {1210setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);12111212return testCloneFile();1213});12141215async function testCloneFile(): Promise<void> {1216const source1 = URI.file(join(testDir, 'index.html'));1217const source1Size = (await service.resolve(source1, { resolveMetadata: true })).size;12181219const source2 = URI.file(join(testDir, 'lorem.txt'));1220const source2Size = (await service.resolve(source2, { resolveMetadata: true })).size;12211222const targetParent = URI.file(testDir);12231224// same path is a no-op1225await service.cloneFile(source1, source1);12261227// simple clone to existing parent folder path1228const target1 = targetParent.with({ path: posix.join(targetParent.path, `${posix.basename(source1.path)}-clone`) });12291230await service.cloneFile(source1, URI.file(target1.fsPath));12311232assert.strictEqual(existsSync(target1.fsPath), true);1233assert.strictEqual(basename(target1.fsPath), 'index.html-clone');12341235let target1Size = (await service.resolve(target1, { resolveMetadata: true })).size;12361237assert.strictEqual(source1Size, target1Size);12381239// clone to same path overwrites1240await service.cloneFile(source2, URI.file(target1.fsPath));12411242target1Size = (await service.resolve(target1, { resolveMetadata: true })).size;12431244assert.strictEqual(source2Size, target1Size);1245assert.notStrictEqual(source1Size, target1Size);12461247// clone creates missing folders ad-hoc1248const target2 = targetParent.with({ path: posix.join(targetParent.path, 'foo', 'bar', `${posix.basename(source1.path)}-clone`) });12491250await service.cloneFile(source1, URI.file(target2.fsPath));12511252assert.strictEqual(existsSync(target2.fsPath), true);1253assert.strictEqual(basename(target2.fsPath), 'index.html-clone');12541255const target2Size = (await service.resolve(target2, { resolveMetadata: true })).size;12561257assert.strictEqual(source1Size, target2Size);1258}12591260test('readFile - small file - default', () => {1261return testReadFile(URI.file(join(testDir, 'small.txt')));1262});12631264test('readFile - small file - buffered', () => {1265setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);12661267return testReadFile(URI.file(join(testDir, 'small.txt')));1268});12691270test('readFile - small file - buffered / readonly', () => {1271setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);12721273return testReadFile(URI.file(join(testDir, 'small.txt')));1274});12751276test('readFile - small file - unbuffered', async () => {1277setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);12781279return testReadFile(URI.file(join(testDir, 'small.txt')));1280});12811282test('readFile - small file - unbuffered / readonly', async () => {1283setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);12841285return testReadFile(URI.file(join(testDir, 'small.txt')));1286});12871288test('readFile - small file - streamed', async () => {1289setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);12901291return testReadFile(URI.file(join(testDir, 'small.txt')));1292});12931294test('readFile - small file - streamed / readonly', async () => {1295setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream | FileSystemProviderCapabilities.Readonly);12961297return testReadFile(URI.file(join(testDir, 'small.txt')));1298});12991300test('readFile - large file - default', async () => {1301return testReadFile(URI.file(join(testDir, 'lorem.txt')));1302});13031304test('readFile - large file - buffered', async () => {1305setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);13061307return testReadFile(URI.file(join(testDir, 'lorem.txt')));1308});13091310test('readFile - large file - unbuffered', async () => {1311setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);13121313return testReadFile(URI.file(join(testDir, 'lorem.txt')));1314});13151316test('readFile - large file - streamed', async () => {1317setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13181319return testReadFile(URI.file(join(testDir, 'lorem.txt')));1320});13211322test('readFile - atomic (emulated on service level)', async () => {1323setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13241325return testReadFile(URI.file(join(testDir, 'lorem.txt')), { atomic: true });1326});13271328test('readFile - atomic (natively supported)', async () => {1329setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite & FileSystemProviderCapabilities.FileAtomicRead);13301331return testReadFile(URI.file(join(testDir, 'lorem.txt')), { atomic: true });1332});13331334async function testReadFile(resource: URI, options?: IReadFileOptions): Promise<void> {1335const content = await service.readFile(resource, options);13361337assert.strictEqual(content.value.toString(), readFileSync(resource.fsPath).toString());1338}13391340test('readFileStream - small file - default', () => {1341return testReadFileStream(URI.file(join(testDir, 'small.txt')));1342});13431344test('readFileStream - small file - buffered', () => {1345setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);13461347return testReadFileStream(URI.file(join(testDir, 'small.txt')));1348});13491350test('readFileStream - small file - unbuffered', async () => {1351setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);13521353return testReadFileStream(URI.file(join(testDir, 'small.txt')));1354});13551356test('readFileStream - small file - streamed', async () => {1357setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13581359return testReadFileStream(URI.file(join(testDir, 'small.txt')));1360});13611362async function testReadFileStream(resource: URI): Promise<void> {1363const content = await service.readFileStream(resource);13641365assert.strictEqual((await streamToBuffer(content.value)).toString(), readFileSync(resource.fsPath).toString());1366}13671368test('readFile - Files are intermingled #38331 - default', async () => {1369return testFilesNotIntermingled();1370});13711372test('readFile - Files are intermingled #38331 - buffered', async () => {1373setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);13741375return testFilesNotIntermingled();1376});13771378test('readFile - Files are intermingled #38331 - unbuffered', async () => {1379setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);13801381return testFilesNotIntermingled();1382});13831384test('readFile - Files are intermingled #38331 - streamed', async () => {1385setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);13861387return testFilesNotIntermingled();1388});13891390async function testFilesNotIntermingled() {1391const resource1 = URI.file(join(testDir, 'lorem.txt'));1392const resource2 = URI.file(join(testDir, 'some_utf16le.css'));13931394// load in sequence and keep data1395const value1 = await service.readFile(resource1);1396const value2 = await service.readFile(resource2);13971398// load in parallel in expect the same result1399const result = await Promise.all([1400service.readFile(resource1),1401service.readFile(resource2)1402]);14031404assert.strictEqual(result[0].value.toString(), value1.value.toString());1405assert.strictEqual(result[1].value.toString(), value2.value.toString());1406}14071408test('readFile - from position (ASCII) - default', async () => {1409return testReadFileFromPositionAscii();1410});14111412test('readFile - from position (ASCII) - buffered', async () => {1413setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);14141415return testReadFileFromPositionAscii();1416});14171418test('readFile - from position (ASCII) - unbuffered', async () => {1419setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);14201421return testReadFileFromPositionAscii();1422});14231424test('readFile - from position (ASCII) - streamed', async () => {1425setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);14261427return testReadFileFromPositionAscii();1428});14291430async function testReadFileFromPositionAscii() {1431const resource = URI.file(join(testDir, 'small.txt'));14321433const contents = await service.readFile(resource, { position: 6 });14341435assert.strictEqual(contents.value.toString(), 'File');1436}14371438test('readFile - from position (with umlaut) - default', async () => {1439return testReadFileFromPositionUmlaut();1440});14411442test('readFile - from position (with umlaut) - buffered', async () => {1443setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);14441445return testReadFileFromPositionUmlaut();1446});14471448test('readFile - from position (with umlaut) - unbuffered', async () => {1449setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);14501451return testReadFileFromPositionUmlaut();1452});14531454test('readFile - from position (with umlaut) - streamed', async () => {1455setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);14561457return testReadFileFromPositionUmlaut();1458});14591460async function testReadFileFromPositionUmlaut() {1461const resource = URI.file(join(testDir, 'small_umlaut.txt'));14621463const contents = await service.readFile(resource, { position: Buffer.from('Small File with Ü').length });14641465assert.strictEqual(contents.value.toString(), 'mlaut');1466}14671468test('readFile - 3 bytes (ASCII) - default', async () => {1469return testReadThreeBytesFromFile();1470});14711472test('readFile - 3 bytes (ASCII) - buffered', async () => {1473setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);14741475return testReadThreeBytesFromFile();1476});14771478test('readFile - 3 bytes (ASCII) - unbuffered', async () => {1479setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);14801481return testReadThreeBytesFromFile();1482});14831484test('readFile - 3 bytes (ASCII) - streamed', async () => {1485setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);14861487return testReadThreeBytesFromFile();1488});14891490async function testReadThreeBytesFromFile() {1491const resource = URI.file(join(testDir, 'small.txt'));14921493const contents = await service.readFile(resource, { length: 3 });14941495assert.strictEqual(contents.value.toString(), 'Sma');1496}14971498test('readFile - 20000 bytes (large) - default', async () => {1499return readLargeFileWithLength(20000);1500});15011502test('readFile - 20000 bytes (large) - buffered', async () => {1503setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);15041505return readLargeFileWithLength(20000);1506});15071508test('readFile - 20000 bytes (large) - unbuffered', async () => {1509setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);15101511return readLargeFileWithLength(20000);1512});15131514test('readFile - 20000 bytes (large) - streamed', async () => {1515setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);15161517return readLargeFileWithLength(20000);1518});15191520test('readFile - 80000 bytes (large) - default', async () => {1521return readLargeFileWithLength(80000);1522});15231524test('readFile - 80000 bytes (large) - buffered', async () => {1525setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);15261527return readLargeFileWithLength(80000);1528});15291530test('readFile - 80000 bytes (large) - unbuffered', async () => {1531setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);15321533return readLargeFileWithLength(80000);1534});15351536test('readFile - 80000 bytes (large) - streamed', async () => {1537setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);15381539return readLargeFileWithLength(80000);1540});15411542async function readLargeFileWithLength(length: number) {1543const resource = URI.file(join(testDir, 'lorem.txt'));15441545const contents = await service.readFile(resource, { length });15461547assert.strictEqual(contents.value.byteLength, length);1548}15491550test('readFile - FILE_IS_DIRECTORY', async () => {1551const resource = URI.file(join(testDir, 'deep'));15521553let error: FileOperationError | undefined = undefined;1554try {1555await service.readFile(resource);1556} catch (err) {1557error = err;1558}15591560assert.ok(error);1561assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_IS_DIRECTORY);1562});15631564(isWindows /* error code does not seem to be supported on windows */ ? test.skip : test)('readFile - FILE_NOT_DIRECTORY', async () => {1565const resource = URI.file(join(testDir, 'lorem.txt', 'file.txt'));15661567let error: FileOperationError | undefined = undefined;1568try {1569await service.readFile(resource);1570} catch (err) {1571error = err;1572}15731574assert.ok(error);1575assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_DIRECTORY);1576});15771578test('readFile - FILE_NOT_FOUND', async () => {1579const resource = URI.file(join(testDir, '404.html'));15801581let error: FileOperationError | undefined = undefined;1582try {1583await service.readFile(resource);1584} catch (err) {1585error = err;1586}15871588assert.ok(error);1589assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_FOUND);1590});15911592test('readFile - FILE_NOT_MODIFIED_SINCE - default', async () => {1593return testNotModifiedSince();1594});15951596test('readFile - FILE_NOT_MODIFIED_SINCE - buffered', async () => {1597setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);15981599return testNotModifiedSince();1600});16011602test('readFile - FILE_NOT_MODIFIED_SINCE - unbuffered', async () => {1603setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);16041605return testNotModifiedSince();1606});16071608test('readFile - FILE_NOT_MODIFIED_SINCE - streamed', async () => {1609setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);16101611return testNotModifiedSince();1612});16131614async function testNotModifiedSince() {1615const resource = URI.file(join(testDir, 'index.html'));16161617const contents = await service.readFile(resource);1618fileProvider.totalBytesRead = 0;16191620let error: FileOperationError | undefined = undefined;1621try {1622await service.readFile(resource, { etag: contents.etag });1623} catch (err) {1624error = err;1625}16261627assert.ok(error);1628assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);1629assert.ok(error instanceof NotModifiedSinceFileOperationError && error.stat);1630assert.strictEqual(fileProvider.totalBytesRead, 0);1631}16321633test('readFile - FILE_NOT_MODIFIED_SINCE does not fire wrongly - https://github.com/microsoft/vscode/issues/72909', async () => {1634fileProvider.setInvalidStatSize(true);16351636const resource = URI.file(join(testDir, 'index.html'));16371638await service.readFile(resource);16391640let error: FileOperationError | undefined = undefined;1641try {1642await service.readFile(resource, { etag: undefined });1643} catch (err) {1644error = err;1645}16461647assert.ok(!error);1648});16491650test('readFile - FILE_TOO_LARGE - default', async () => {1651return testFileTooLarge();1652});16531654test('readFile - FILE_TOO_LARGE - buffered', async () => {1655setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);16561657return testFileTooLarge();1658});16591660test('readFile - FILE_TOO_LARGE - unbuffered', async () => {1661setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);16621663return testFileTooLarge();1664});16651666test('readFile - FILE_TOO_LARGE - streamed', async () => {1667setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream);16681669return testFileTooLarge();1670});16711672async function testFileTooLarge() {1673await doTestFileTooLarge(false);16741675// Also test when the stat size is wrong1676fileProvider.setSmallStatSize(true);1677return doTestFileTooLarge(true);1678}16791680async function doTestFileTooLarge(statSizeWrong: boolean) {1681const resource = URI.file(join(testDir, 'index.html'));16821683let error: FileOperationError | undefined = undefined;1684try {1685await service.readFile(resource, { limits: { size: 10 } });1686} catch (err) {1687error = err;1688}16891690if (!statSizeWrong) {1691assert.ok(error instanceof TooLargeFileOperationError);1692assert.ok(typeof error.size === 'number');1693}1694assert.strictEqual(error!.fileOperationResult, FileOperationResult.FILE_TOO_LARGE);1695}16961697(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 () => {1698const link = URI.file(join(testDir, 'small.js-link'));1699await promises.symlink(join(testDir, 'small.js'), link.fsPath);17001701let error: FileOperationError | undefined = undefined;1702try {1703await service.readFile(link);1704} catch (err) {1705error = err;1706}17071708assert.ok(error);1709});17101711test('createFile', async () => {1712return assertCreateFile(contents => VSBuffer.fromString(contents));1713});17141715test('createFile (readable)', async () => {1716return assertCreateFile(contents => bufferToReadable(VSBuffer.fromString(contents)));1717});17181719test('createFile (stream)', async () => {1720return assertCreateFile(contents => bufferToStream(VSBuffer.fromString(contents)));1721});17221723async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> {1724let event: FileOperationEvent;1725disposables.add(service.onDidRunOperation(e => event = e));17261727const contents = 'Hello World';1728const resource = URI.file(join(testDir, 'test.txt'));17291730assert.strictEqual(await service.canCreateFile(resource), true);1731const fileStat = await service.createFile(resource, converter(contents));1732assert.strictEqual(fileStat.name, 'test.txt');1733assert.strictEqual(existsSync(fileStat.resource.fsPath), true);1734assert.strictEqual(readFileSync(fileStat.resource.fsPath).toString(), contents);17351736assert.ok(event!);1737assert.strictEqual(event!.resource.fsPath, resource.fsPath);1738assert.strictEqual(event!.operation, FileOperation.CREATE);1739assert.strictEqual(event!.target!.resource.fsPath, resource.fsPath);1740}17411742test('createFile (does not overwrite by default)', async () => {1743const contents = 'Hello World';1744const resource = URI.file(join(testDir, 'test.txt'));17451746writeFileSync(resource.fsPath, ''); // create file17471748assert.ok((await service.canCreateFile(resource)) instanceof Error);17491750let error;1751try {1752await service.createFile(resource, VSBuffer.fromString(contents));1753} catch (err) {1754error = err;1755}17561757assert.ok(error);1758});17591760test('createFile (allows to overwrite existing)', async () => {1761let event: FileOperationEvent;1762disposables.add(service.onDidRunOperation(e => event = e));17631764const contents = 'Hello World';1765const resource = URI.file(join(testDir, 'test.txt'));17661767writeFileSync(resource.fsPath, ''); // create file17681769assert.strictEqual(await service.canCreateFile(resource, { overwrite: true }), true);1770const fileStat = await service.createFile(resource, VSBuffer.fromString(contents), { overwrite: true });1771assert.strictEqual(fileStat.name, 'test.txt');1772assert.strictEqual(existsSync(fileStat.resource.fsPath), true);1773assert.strictEqual(readFileSync(fileStat.resource.fsPath).toString(), contents);17741775assert.ok(event!);1776assert.strictEqual(event!.resource.fsPath, resource.fsPath);1777assert.strictEqual(event!.operation, FileOperation.CREATE);1778assert.strictEqual(event!.target!.resource.fsPath, resource.fsPath);1779});17801781test('writeFile - default', async () => {1782return testWriteFile(false);1783});17841785test('writeFile - flush on write', async () => {1786DiskFileSystemProvider.configureFlushOnWrite(true);1787try {1788return await testWriteFile(false);1789} finally {1790DiskFileSystemProvider.configureFlushOnWrite(false);1791}1792});17931794test('writeFile - buffered', async () => {1795setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);17961797return testWriteFile(false);1798});17991800test('writeFile - unbuffered', async () => {1801setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);18021803return testWriteFile(false);1804});18051806test('writeFile - default (atomic)', async () => {1807return testWriteFile(true);1808});18091810test('writeFile - flush on write (atomic)', async () => {1811DiskFileSystemProvider.configureFlushOnWrite(true);1812try {1813return await testWriteFile(true);1814} finally {1815DiskFileSystemProvider.configureFlushOnWrite(false);1816}1817});18181819test('writeFile - buffered (atomic)', async () => {1820setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAtomicWrite);18211822let e;1823try {1824await testWriteFile(true);1825} catch (error) {1826e = error;1827}18281829assert.ok(e);1830});18311832test('writeFile - unbuffered (atomic)', async () => {1833setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);18341835return testWriteFile(true);1836});18371838(isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('writeFile - atomic writing does not break symlinks', async () => {1839const link = URI.file(join(testDir, 'lorem.txt-linked'));1840await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath);18411842const content = 'Updates to the lorem file';1843await service.writeFile(link, VSBuffer.fromString(content), { atomic: { postfix: '.vsctmp' } });1844assert.strictEqual(readFileSync(link.fsPath).toString(), content);18451846const resolved = await service.resolve(link);1847assert.strictEqual(resolved.isSymbolicLink, true);1848});18491850async function testWriteFile(atomic: boolean) {1851let event: FileOperationEvent;1852disposables.add(service.onDidRunOperation(e => event = e));18531854const resource = URI.file(join(testDir, 'small.txt'));18551856const content = readFileSync(resource.fsPath).toString();1857assert.strictEqual(content, 'Small File');18581859const newContent = 'Updates to the small file';1860await service.writeFile(resource, VSBuffer.fromString(newContent), { atomic: atomic ? { postfix: '.vsctmp' } : false });18611862assert.ok(event!);1863assert.strictEqual(event!.resource.fsPath, resource.fsPath);1864assert.strictEqual(event!.operation, FileOperation.WRITE);18651866assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);1867}18681869test('writeFile (large file) - default', async () => {1870return testWriteFileLarge(false);1871});18721873test('writeFile (large file) - buffered', async () => {1874setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);18751876return testWriteFileLarge(false);1877});18781879test('writeFile (large file) - unbuffered', async () => {1880setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);18811882return testWriteFileLarge(false);1883});18841885test('writeFile (large file) - default (atomic)', async () => {1886return testWriteFileLarge(true);1887});18881889test('writeFile (large file) - buffered (atomic)', async () => {1890setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAtomicWrite);18911892let e;1893try {1894await testWriteFileLarge(true);1895} catch (error) {1896e = error;1897}18981899assert.ok(e);1900});19011902test('writeFile (large file) - unbuffered (atomic)', async () => {1903setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);19041905return testWriteFileLarge(true);1906});19071908async function testWriteFileLarge(atomic: boolean) {1909const resource = URI.file(join(testDir, 'lorem.txt'));19101911const content = readFileSync(resource.fsPath);1912const newContent = content.toString() + content.toString();19131914const fileStat = await service.writeFile(resource, VSBuffer.fromString(newContent), { atomic: atomic ? { postfix: '.vsctmp' } : false });1915assert.strictEqual(fileStat.name, 'lorem.txt');19161917assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);1918}19191920test('writeFile (large file) - unbuffered (atomic) - concurrent writes with multiple services', async () => {1921setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAtomicWrite);19221923const resource = URI.file(join(testDir, 'lorem.txt'));19241925const content = readFileSync(resource.fsPath);1926const newContent = content.toString() + content.toString();19271928const promises: Promise<IFileStatWithMetadata>[] = [];1929let suffix = 0;1930for (let i = 0; i < 10; i++) {1931const service = disposables.add(new FileService(new NullLogService()));1932disposables.add(service.registerProvider(Schemas.file, fileProvider));19331934promises.push(service.writeFile(resource, VSBuffer.fromString(`${newContent}${++suffix}`), { atomic: { postfix: '.vsctmp' } }));1935await timeout(0);1936}19371938await Promise.allSettled(promises);19391940assert.strictEqual(readFileSync(resource.fsPath).toString(), `${newContent}${suffix}`);1941});19421943test('writeFile - buffered - readonly throws', async () => {1944setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);19451946return testWriteFileReadonlyThrows();1947});19481949test('writeFile - unbuffered - readonly throws', async () => {1950setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);19511952return testWriteFileReadonlyThrows();1953});19541955async function testWriteFileReadonlyThrows() {1956const resource = URI.file(join(testDir, 'small.txt'));19571958const content = readFileSync(resource.fsPath).toString();1959assert.strictEqual(content, 'Small File');19601961const newContent = 'Updates to the small file';19621963let error: Error;1964try {1965await service.writeFile(resource, VSBuffer.fromString(newContent));1966} catch (err) {1967error = err;1968}19691970assert.ok(error!);1971}19721973test('writeFile (large file) - multiple parallel writes queue up and atomic read support (via file service)', async () => {1974const resource = URI.file(join(testDir, 'lorem.txt'));19751976const content = readFileSync(resource.fsPath);1977const newContent = content.toString() + content.toString();19781979const writePromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {1980const fileStat = await service.writeFile(resource, VSBuffer.fromString(offset + newContent));1981assert.strictEqual(fileStat.name, 'lorem.txt');1982}));19831984const readPromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async () => {1985const fileContent = await service.readFile(resource, { atomic: true });1986assert.ok(fileContent.value.byteLength > 0); // `atomic: true` ensures we never read a truncated file1987}));19881989await Promise.all([writePromises, readPromises]);1990});19911992test('provider - write barrier prevents dirty writes', async () => {1993const resource = URI.file(join(testDir, 'lorem.txt'));19941995const content = readFileSync(resource.fsPath);1996const newContent = content.toString() + content.toString();19971998const provider = service.getProvider(resource.scheme);1999assert.ok(provider);2000assert.ok(hasOpenReadWriteCloseCapability(provider));20012002const writePromises = Promise.all(['0', '00', '000', '0000', '00000'].map(async offset => {2003const content = offset + newContent;2004const contentBuffer = VSBuffer.fromString(content).buffer;20052006const fd = await provider.open(resource, { create: true, unlock: false });2007try {2008await provider.write(fd, 0, VSBuffer.fromString(content).buffer, 0, contentBuffer.byteLength);20092010// Here since `close` is not called, all other writes are2011// waiting on the barrier to release, so doing a readFile2012// should give us a consistent view of the file contents2013assert.strictEqual((await promises.readFile(resource.fsPath)).toString(), content);2014} finally {2015await provider.close(fd);2016}2017}));20182019await Promise.all([writePromises]);2020});20212022test('provider - write barrier is partitioned per resource', async () => {2023const resource1 = URI.file(join(testDir, 'lorem.txt'));2024const resource2 = URI.file(join(testDir, 'test.txt'));20252026const provider = service.getProvider(resource1.scheme);2027assert.ok(provider);2028assert.ok(hasOpenReadWriteCloseCapability(provider));20292030const fd1 = await provider.open(resource1, { create: true, unlock: false });2031const fd2 = await provider.open(resource2, { create: true, unlock: false });20322033const newContent = 'Hello World';20342035try {2036await provider.write(fd1, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength);2037assert.strictEqual((await promises.readFile(resource1.fsPath)).toString(), newContent);20382039await provider.write(fd2, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength);2040assert.strictEqual((await promises.readFile(resource2.fsPath)).toString(), newContent);2041} finally {2042await Promise.allSettled([2043await provider.close(fd1),2044await provider.close(fd2)2045]);2046}2047});20482049test('provider - write barrier not becoming stale', async () => {2050const newFolder = join(testDir, 'new-folder');2051const newResource = URI.file(join(newFolder, 'lorem.txt'));20522053const provider = service.getProvider(newResource.scheme);2054assert.ok(provider);2055assert.ok(hasOpenReadWriteCloseCapability(provider));20562057let error: Error | undefined = undefined;2058try {2059await provider.open(newResource, { create: true, unlock: false });2060} catch (e) {2061error = e;2062}20632064assert.ok(error); // expected because `new-folder` does not exist20652066await promises.mkdir(newFolder);20672068const content = readFileSync(URI.file(join(testDir, 'lorem.txt')).fsPath);2069const newContent = content.toString() + content.toString();2070const newContentBuffer = VSBuffer.fromString(newContent).buffer;20712072const fd = await provider.open(newResource, { create: true, unlock: false });2073try {2074await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);20752076assert.strictEqual((await promises.readFile(newResource.fsPath)).toString(), newContent);2077} finally {2078await provider.close(fd);2079}2080});20812082test('provider - atomic reads (write pending when read starts)', async () => {2083const resource = URI.file(join(testDir, 'lorem.txt'));20842085const content = readFileSync(resource.fsPath);2086const newContent = content.toString() + content.toString();2087const newContentBuffer = VSBuffer.fromString(newContent).buffer;20882089const provider = service.getProvider(resource.scheme);2090assert.ok(provider);2091assert.ok(hasOpenReadWriteCloseCapability(provider));2092assert.ok(hasFileAtomicReadCapability(provider));20932094let atomicReadPromise: Promise<Uint8Array> | undefined = undefined;2095const fd = await provider.open(resource, { create: true, unlock: false });2096try {20972098// Start reading while write is pending2099atomicReadPromise = provider.readFile(resource, { atomic: true });21002101// Simulate a slow write, giving the read2102// a chance to succeed if it were not atomic2103await timeout(20);21042105await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);2106} finally {2107await provider.close(fd);2108}21092110assert.ok(atomicReadPromise);21112112const atomicReadResult = await atomicReadPromise;2113assert.strictEqual(atomicReadResult.byteLength, newContentBuffer.byteLength);2114});21152116test('provider - atomic reads (read pending when write starts)', async () => {2117const resource = URI.file(join(testDir, 'lorem.txt'));21182119const content = readFileSync(resource.fsPath);2120const newContent = content.toString() + content.toString();2121const newContentBuffer = VSBuffer.fromString(newContent).buffer;21222123const provider = service.getProvider(resource.scheme);2124assert.ok(provider);2125assert.ok(hasOpenReadWriteCloseCapability(provider));2126assert.ok(hasFileAtomicReadCapability(provider));21272128let atomicReadPromise = provider.readFile(resource, { atomic: true });21292130const fdPromise = provider.open(resource, { create: true, unlock: false }).then(async fd => {2131try {2132return await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength);2133} finally {2134await provider.close(fd);2135}2136});21372138let atomicReadResult = await atomicReadPromise;2139assert.strictEqual(atomicReadResult.byteLength, content.byteLength);21402141await fdPromise;21422143atomicReadPromise = provider.readFile(resource, { atomic: true });2144atomicReadResult = await atomicReadPromise;2145assert.strictEqual(atomicReadResult.byteLength, newContentBuffer.byteLength);2146});21472148test('writeFile (readable) - default', async () => {2149return testWriteFileReadable();2150});21512152test('writeFile (readable) - buffered', async () => {2153setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);21542155return testWriteFileReadable();2156});21572158test('writeFile (readable) - unbuffered', async () => {2159setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);21602161return testWriteFileReadable();2162});21632164async function testWriteFileReadable() {2165const resource = URI.file(join(testDir, 'small.txt'));21662167const content = readFileSync(resource.fsPath).toString();2168assert.strictEqual(content, 'Small File');21692170const newContent = 'Updates to the small file';2171await service.writeFile(resource, toLineByLineReadable(newContent));21722173assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);2174}21752176test('writeFile (large file - readable) - default', async () => {2177return testWriteFileLargeReadable();2178});21792180test('writeFile (large file - readable) - buffered', async () => {2181setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);21822183return testWriteFileLargeReadable();2184});21852186test('writeFile (large file - readable) - unbuffered', async () => {2187setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);21882189return testWriteFileLargeReadable();2190});21912192async function testWriteFileLargeReadable() {2193const resource = URI.file(join(testDir, 'lorem.txt'));21942195const content = readFileSync(resource.fsPath);2196const newContent = content.toString() + content.toString();21972198const fileStat = await service.writeFile(resource, toLineByLineReadable(newContent));2199assert.strictEqual(fileStat.name, 'lorem.txt');22002201assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);2202}22032204test('writeFile (stream) - default', async () => {2205return testWriteFileStream();2206});22072208test('writeFile (stream) - buffered', async () => {2209setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);22102211return testWriteFileStream();2212});22132214test('writeFile (stream) - unbuffered', async () => {2215setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);22162217return testWriteFileStream();2218});22192220async function testWriteFileStream() {2221const source = URI.file(join(testDir, 'small.txt'));2222const target = URI.file(join(testDir, 'small-copy.txt'));22232224const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath)));2225assert.strictEqual(fileStat.name, 'small-copy.txt');22262227const targetContents = readFileSync(target.fsPath).toString();2228assert.strictEqual(readFileSync(source.fsPath).toString(), targetContents);2229}22302231test('writeFile (large file - stream) - default', async () => {2232return testWriteFileLargeStream();2233});22342235test('writeFile (large file - stream) - buffered', async () => {2236setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);22372238return testWriteFileLargeStream();2239});22402241test('writeFile (large file - stream) - unbuffered', async () => {2242setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);22432244return testWriteFileLargeStream();2245});22462247async function testWriteFileLargeStream() {2248const source = URI.file(join(testDir, 'lorem.txt'));2249const target = URI.file(join(testDir, 'lorem-copy.txt'));22502251const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath)));2252assert.strictEqual(fileStat.name, 'lorem-copy.txt');22532254const targetContents = readFileSync(target.fsPath).toString();2255assert.strictEqual(readFileSync(source.fsPath).toString(), targetContents);2256}22572258test('writeFile (file is created including parents)', async () => {2259const resource = URI.file(join(testDir, 'other', 'newfile.txt'));22602261const content = 'File is created including parent';2262const fileStat = await service.writeFile(resource, VSBuffer.fromString(content));2263assert.strictEqual(fileStat.name, 'newfile.txt');22642265assert.strictEqual(readFileSync(resource.fsPath).toString(), content);2266});22672268test('writeFile - locked files and unlocking', async () => {2269setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileWriteUnlock);22702271return testLockedFiles(false);2272});22732274test('writeFile (stream) - locked files and unlocking', async () => {2275setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileWriteUnlock);22762277return testLockedFiles(false);2278});22792280test('writeFile - locked files and unlocking throws error when missing capability', async () => {2281setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);22822283return testLockedFiles(true);2284});22852286test('writeFile (stream) - locked files and unlocking throws error when missing capability', async () => {2287setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);22882289return testLockedFiles(true);2290});22912292async function testLockedFiles(expectError: boolean) {2293const lockedFile = URI.file(join(testDir, 'my-locked-file'));22942295const content = await service.writeFile(lockedFile, VSBuffer.fromString('Locked File'));2296assert.strictEqual(content.locked, false);22972298const stats = await promises.stat(lockedFile.fsPath);2299await promises.chmod(lockedFile.fsPath, stats.mode & ~0o200);23002301let stat = await service.stat(lockedFile);2302assert.strictEqual(stat.locked, true);23032304let error;2305const newContent = 'Updates to locked file';2306try {2307await service.writeFile(lockedFile, VSBuffer.fromString(newContent));2308} catch (e) {2309error = e;2310}23112312assert.ok(error);2313error = undefined;23142315if (expectError) {2316try {2317await service.writeFile(lockedFile, VSBuffer.fromString(newContent), { unlock: true });2318} catch (e) {2319error = e;2320}23212322assert.ok(error);2323} else {2324await service.writeFile(lockedFile, VSBuffer.fromString(newContent), { unlock: true });2325assert.strictEqual(readFileSync(lockedFile.fsPath).toString(), newContent);23262327stat = await service.stat(lockedFile);2328assert.strictEqual(stat.locked, false);2329}2330}23312332test('writeFile (error when folder is encountered)', async () => {2333const resource = URI.file(testDir);23342335let error: Error | undefined = undefined;2336try {2337await service.writeFile(resource, VSBuffer.fromString('File is created including parent'));2338} catch (err) {2339error = err;2340}23412342assert.ok(error);2343});23442345test('writeFile (no error when providing up to date etag)', async () => {2346const resource = URI.file(join(testDir, 'small.txt'));23472348const stat = await service.resolve(resource);23492350const content = readFileSync(resource.fsPath).toString();2351assert.strictEqual(content, 'Small File');23522353const newContent = 'Updates to the small file';2354await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });23552356assert.strictEqual(readFileSync(resource.fsPath).toString(), newContent);2357});23582359test('writeFile - error when writing to file that has been updated meanwhile', async () => {2360const resource = URI.file(join(testDir, 'small.txt'));23612362const stat = await service.resolve(resource);23632364const content = readFileSync(resource.fsPath).toString();2365assert.strictEqual(content, 'Small File');23662367const newContent = 'Updates to the small file';2368await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });23692370const newContentLeadingToError = newContent + newContent;23712372const fakeMtime = 1000;2373const fakeSize = 1000;23742375let error: FileOperationError | undefined = undefined;2376try {2377await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToError), { etag: etag({ mtime: fakeMtime, size: fakeSize }), mtime: fakeMtime });2378} catch (err) {2379error = err;2380}23812382assert.ok(error);2383assert.ok(error instanceof FileOperationError);2384assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);2385});23862387test('writeFile - no error when writing to file where size is the same', async () => {2388const resource = URI.file(join(testDir, 'small.txt'));23892390const stat = await service.resolve(resource);23912392const content = readFileSync(resource.fsPath).toString();2393assert.strictEqual(content, 'Small File');23942395const newContent = content; // same content2396await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });23972398const newContentLeadingToNoError = newContent; // writing the same content should be OK23992400const fakeMtime = 1000;2401const actualSize = newContent.length;24022403let error: FileOperationError | undefined = undefined;2404try {2405await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToNoError), { etag: etag({ mtime: fakeMtime, size: actualSize }), mtime: fakeMtime });2406} catch (err) {2407error = err;2408}24092410assert.ok(!error);2411});24122413test('writeFile - no error when writing to file where content is the same', async () => {2414const resource = URI.file(join(testDir, 'small.txt'));24152416await service.resolve(resource);24172418const content = readFileSync(resource.fsPath).toString();2419assert.strictEqual(content, 'Small File');24202421const newContent = content; // same content2422let error: FileOperationError | undefined = undefined;2423try {2424await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: 'anything', mtime: 0 } /* fake it */);2425} catch (err) {2426error = err;2427}24282429assert.ok(!error);2430});24312432test('writeFile - error when writing to file where content is the same length but different', 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.split('').reverse().join(''); // reverse 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);2449assert.ok(error instanceof FileOperationError);2450assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);2451});24522453test('writeFile - no error when writing to same nonexistent folder multiple times different new files', async () => {2454const newFolder = URI.file(join(testDir, 'some', 'new', 'folder'));24552456const file1 = joinPath(newFolder, 'file-1');2457const file2 = joinPath(newFolder, 'file-2');2458const file3 = joinPath(newFolder, 'file-3');24592460// this essentially verifies that the mkdirp logic implemented2461// in the file service is able to receive multiple requests for2462// the same folder and will not throw errors if another racing2463// call succeeded first.2464const newContent = 'Updates to the small file';2465await Promise.all([2466service.writeFile(file1, VSBuffer.fromString(newContent)),2467service.writeFile(file2, VSBuffer.fromString(newContent)),2468service.writeFile(file3, VSBuffer.fromString(newContent))2469]);24702471assert.ok(service.exists(file1));2472assert.ok(service.exists(file2));2473assert.ok(service.exists(file3));2474});24752476test('writeFile - error when writing to folder that is a file', async () => {2477const existingFile = URI.file(join(testDir, 'my-file'));24782479await service.createFile(existingFile);24802481const newFile = joinPath(existingFile, 'file-1');24822483let error;2484const newContent = 'Updates to the small file';2485try {2486await service.writeFile(newFile, VSBuffer.fromString(newContent));2487} catch (e) {2488error = e;2489}24902491assert.ok(error);2492});24932494test('read - mixed positions', async () => {2495const resource = URI.file(join(testDir, 'lorem.txt'));24962497// read multiple times from position 02498let buffer = VSBuffer.alloc(1024);2499let fd = await fileProvider.open(resource, { create: false });2500for (let i = 0; i < 3; i++) {2501await fileProvider.read(fd, 0, buffer.buffer, 0, 26);2502assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');2503}2504await fileProvider.close(fd);25052506// read multiple times at various locations2507buffer = VSBuffer.alloc(1024);2508fd = await fileProvider.open(resource, { create: false });25092510let posInFile = 0;25112512await fileProvider.read(fd, posInFile, buffer.buffer, 0, 26);2513assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');2514posInFile += 26;25152516await fileProvider.read(fd, posInFile, buffer.buffer, 0, 1);2517assert.strictEqual(buffer.slice(0, 1).toString(), ',');2518posInFile += 1;25192520await fileProvider.read(fd, posInFile, buffer.buffer, 0, 12);2521assert.strictEqual(buffer.slice(0, 12).toString(), ' consectetur');2522posInFile += 12;25232524await fileProvider.read(fd, 98 /* no longer in sequence of posInFile */, buffer.buffer, 0, 9);2525assert.strictEqual(buffer.slice(0, 9).toString(), 'fermentum');25262527await fileProvider.read(fd, 27, buffer.buffer, 0, 12);2528assert.strictEqual(buffer.slice(0, 12).toString(), ' consectetur');25292530await fileProvider.read(fd, 26, buffer.buffer, 0, 1);2531assert.strictEqual(buffer.slice(0, 1).toString(), ',');25322533await fileProvider.read(fd, 0, buffer.buffer, 0, 26);2534assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');25352536await fileProvider.read(fd, posInFile /* back in sequence */, buffer.buffer, 0, 11);2537assert.strictEqual(buffer.slice(0, 11).toString(), ' adipiscing');25382539await fileProvider.close(fd);2540});25412542test('write - mixed positions', async () => {2543const resource = URI.file(join(testDir, 'lorem.txt'));25442545const buffer = VSBuffer.alloc(1024);2546const fdWrite = await fileProvider.open(resource, { create: true, unlock: false });2547const fdRead = await fileProvider.open(resource, { create: false });25482549let posInFileWrite = 0;2550let posInFileRead = 0;25512552const initialContents = VSBuffer.fromString('Lorem ipsum dolor sit amet');2553await fileProvider.write(fdWrite, posInFileWrite, initialContents.buffer, 0, initialContents.byteLength);2554posInFileWrite += initialContents.byteLength;25552556await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, 26);2557assert.strictEqual(buffer.slice(0, 26).toString(), 'Lorem ipsum dolor sit amet');2558posInFileRead += 26;25592560const contents = VSBuffer.fromString('Hello World');25612562await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);2563posInFileWrite += contents.byteLength;25642565await fileProvider.read(fdRead, posInFileRead, buffer.buffer, 0, contents.byteLength);2566assert.strictEqual(buffer.slice(0, contents.byteLength).toString(), 'Hello World');2567posInFileRead += contents.byteLength;25682569await fileProvider.write(fdWrite, 6, contents.buffer, 0, contents.byteLength);25702571await fileProvider.read(fdRead, 0, buffer.buffer, 0, 11);2572assert.strictEqual(buffer.slice(0, 11).toString(), 'Lorem Hello');25732574await fileProvider.write(fdWrite, posInFileWrite, contents.buffer, 0, contents.byteLength);2575posInFileWrite += contents.byteLength;25762577await fileProvider.read(fdRead, posInFileWrite - contents.byteLength, buffer.buffer, 0, contents.byteLength);2578assert.strictEqual(buffer.slice(0, contents.byteLength).toString(), 'Hello World');25792580await fileProvider.close(fdWrite);2581await fileProvider.close(fdRead);2582});25832584test('readonly - is handled properly for a single resource', async () => {2585fileProvider.setReadonly(true);25862587const resource = URI.file(join(testDir, 'index.html'));25882589const resolveResult = await service.resolve(resource);2590assert.strictEqual(resolveResult.readonly, true);25912592const readResult = await service.readFile(resource);2593assert.strictEqual(readResult.readonly, true);25942595let writeFileError: Error | undefined = undefined;2596try {2597await service.writeFile(resource, VSBuffer.fromString('Hello Test'));2598} catch (error) {2599writeFileError = error;2600}2601assert.ok(writeFileError);26022603let deleteFileError: Error | undefined = undefined;2604try {2605await service.del(resource);2606} catch (error) {2607deleteFileError = error;2608}2609assert.ok(deleteFileError);2610});2611});261226132614