Path: blob/main/src/vs/platform/files/test/browser/inmemoryFileService.test.ts
5223 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 { timeout } from '../../../../base/common/async.js';7import { streamToBuffer, VSBuffer } from '../../../../base/common/buffer.js';8import { Schemas } from '../../../../base/common/network.js';9import { joinPath } from '../../../../base/common/resources.js';10import { URI } from '../../../../base/common/uri.js';11import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';12import { FileChangeType, FileOperation, FileOperationEvent, FileSystemProviderCapabilities, IFileStat } from '../../common/files.js';13import { FileService } from '../../common/fileService.js';14import { InMemoryFileSystemProvider } from '../../common/inMemoryFilesystemProvider.js';15import { NullLogService } from '../../../log/common/log.js';1617function getByName(root: IFileStat, name: string): IFileStat | undefined {18if (root.children === undefined) {19return undefined;20}2122return root.children.find(child => child.name === name);23}2425function createLargeBuffer(size: number, seed: number): VSBuffer {26const data = new Uint8Array(size);27for (let i = 0; i < data.length; i++) {28data[i] = (seed + i) % 256;29}3031return VSBuffer.wrap(data);32}3334type Fixture = {35root: URI;36indexHtml: URI;37siteCss: URI;38smallTxt: URI;39smallUmlautTxt: URI;40deepDir: URI;41otherDeepDir: URI;42};4344suite('InMemory File Service', () => {4546const disposables = ensureNoDisposablesAreLeakedInTestSuite();4748let service: FileService;49let provider: InMemoryFileSystemProvider;50let fixture: Fixture;5152setup(async () => {53service = disposables.add(new FileService(new NullLogService()));54provider = disposables.add(new InMemoryFileSystemProvider());55disposables.add(service.registerProvider(Schemas.inMemory, provider));5657fixture = await createFixture(service);58});5960test('createFolder', async () => {61let event: FileOperationEvent | undefined;62disposables.add(service.onDidRunOperation(e => event = e));6364const newFolderResource = joinPath(fixture.root, 'newFolder');65const newFolder = await service.createFolder(newFolderResource);6667assert.strictEqual(newFolder.name, 'newFolder');68assert.strictEqual(await service.exists(newFolderResource), true);6970assert.ok(event);71assert.strictEqual(event.resource.toString(), newFolderResource.toString());72assert.strictEqual(event.operation, FileOperation.CREATE);73assert.strictEqual(event.target?.resource.toString(), newFolderResource.toString());74assert.strictEqual(event.target?.isDirectory, true);75});7677test('createFolder: creating multiple folders at once', async () => {78let event: FileOperationEvent | undefined;79disposables.add(service.onDidRunOperation(e => event = e));8081const multiFolderPaths = ['a', 'couple', 'of', 'folders'];82const newFolderResource = joinPath(fixture.root, ...multiFolderPaths);8384const newFolder = await service.createFolder(newFolderResource);85assert.strictEqual(newFolder.name, multiFolderPaths[multiFolderPaths.length - 1]);86assert.strictEqual(await service.exists(newFolderResource), true);8788assert.ok(event);89assert.strictEqual(event.resource.toString(), newFolderResource.toString());90assert.strictEqual(event.operation, FileOperation.CREATE);91assert.strictEqual(event.target?.resource.toString(), newFolderResource.toString());92assert.strictEqual(event.target?.isDirectory, true);93});9495test('exists', async () => {96let exists = await service.exists(fixture.root);97assert.strictEqual(exists, true);9899exists = await service.exists(joinPath(fixture.root, 'does-not-exist'));100assert.strictEqual(exists, false);101});102103test('resolve - file', async () => {104const resolved = await service.resolve(fixture.indexHtml);105106assert.strictEqual(resolved.name, 'index.html');107assert.strictEqual(resolved.isFile, true);108assert.strictEqual(resolved.isDirectory, false);109});110111test('resolve - directory', async () => {112const resolved = await service.resolve(fixture.root);113assert.strictEqual(resolved.isDirectory, true);114assert.ok(resolved.children);115116const names = resolved.children.map(c => c.name).sort();117assert.deepStrictEqual(names, ['examples', 'index.html', 'other', 'site.css', 'deep', 'small.txt', 'small_umlaut.txt'].sort());118});119120test('resolve - directory with resolveTo', async () => {121const resolved = await service.resolve(fixture.root, { resolveTo: [fixture.deepDir] });122123const deep = getByName(resolved, 'deep');124assert.ok(deep);125assert.ok(deep.children);126assert.strictEqual(deep.children.length, 4);127});128129test('readFile', async () => {130const content = await service.readFile(fixture.smallTxt);131assert.strictEqual(content.value.toString(), 'Small File');132});133134test('readFile - from position (ASCII)', async () => {135const content = await service.readFile(fixture.smallTxt, { position: 6 });136assert.strictEqual(content.value.toString(), 'File');137});138139test('readFile - from position (with umlaut)', async () => {140const pos = VSBuffer.fromString('Small File with Ü').byteLength;141const content = await service.readFile(fixture.smallUmlautTxt, { position: pos });142assert.strictEqual(content.value.toString(), 'mlaut');143});144145test('readFileStream', async () => {146const content = await service.readFileStream(fixture.smallTxt);147assert.strictEqual((await streamToBuffer(content.value)).toString(), 'Small File');148});149150test('writeFile', async () => {151await service.writeFile(fixture.smallTxt, VSBuffer.fromString('Updated'));152153const content = await service.readFile(fixture.smallTxt);154assert.strictEqual(content.value.toString(), 'Updated');155});156157test('provider open/write - append', async () => {158const resource = joinPath(fixture.root, 'append.txt');159await service.writeFile(resource, VSBuffer.fromString('Hello'));160161const fd = await provider.open(resource, { create: true, unlock: false, append: true });162try {163const data = VSBuffer.fromString(' World').buffer;164await provider.write(fd, 0, data, 0, data.byteLength);165} finally {166await provider.close(fd);167}168169const content = await service.readFile(resource);170assert.strictEqual(content.value.toString(), 'Hello World');171});172173test('provider open/write - append (large)', async () => {174const resource = joinPath(fixture.root, 'append-large-open.txt');175const prefix = createLargeBuffer(256 * 1024, 1);176const suffix = createLargeBuffer(256 * 1024, 2);177178await service.writeFile(resource, prefix);179180const fd = await provider.open(resource, { create: true, unlock: false, append: true });181try {182await provider.write(fd, 123 /* ignored in append mode */, suffix.buffer, 0, suffix.byteLength);183} finally {184await provider.close(fd);185}186187const content = await service.readFile(resource);188assert.strictEqual(content.value.byteLength, prefix.byteLength + suffix.byteLength);189190assert.deepStrictEqual(content.value.slice(0, 64).buffer, prefix.slice(0, 64).buffer);191assert.deepStrictEqual(content.value.slice(prefix.byteLength, prefix.byteLength + 64).buffer, suffix.slice(0, 64).buffer);192assert.deepStrictEqual(content.value.slice(content.value.byteLength - 64, content.value.byteLength).buffer, suffix.slice(suffix.byteLength - 64, suffix.byteLength).buffer);193});194195test('writeFile - append', async () => {196const resource = joinPath(fixture.root, 'append-via-writeFile.txt');197await service.writeFile(resource, VSBuffer.fromString('Hello'));198199await service.writeFile(resource, VSBuffer.fromString(' World'), { append: true });200201const content = await service.readFile(resource);202assert.strictEqual(content.value.toString(), 'Hello World');203});204205test('writeFile - append (large)', async () => {206const resource = joinPath(fixture.root, 'append-large-writeFile.txt');207const prefix = createLargeBuffer(256 * 1024, 3);208const suffix = createLargeBuffer(256 * 1024, 4);209210await service.writeFile(resource, prefix);211await service.writeFile(resource, suffix, { append: true });212213const content = await service.readFile(resource);214assert.strictEqual(content.value.byteLength, prefix.byteLength + suffix.byteLength);215216assert.deepStrictEqual(content.value.slice(0, 64).buffer, prefix.slice(0, 64).buffer);217assert.deepStrictEqual(content.value.slice(prefix.byteLength, prefix.byteLength + 64).buffer, suffix.slice(0, 64).buffer);218assert.deepStrictEqual(content.value.slice(content.value.byteLength - 64, content.value.byteLength).buffer, suffix.slice(suffix.byteLength - 64, suffix.byteLength).buffer);219});220221test('rename', async () => {222const source = joinPath(fixture.root, 'site.css');223const target = joinPath(fixture.root, 'SITE.css');224225await service.move(source, target, true);226227assert.strictEqual(await service.exists(source), false);228assert.strictEqual(await service.exists(target), true);229});230231test('copy', async () => {232const source = fixture.indexHtml;233const target = joinPath(fixture.root, 'index-copy.html');234235await service.copy(source, target, true);236237const copied = await service.readFile(target);238assert.strictEqual(copied.value.toString(), 'Index');239});240241test('deleteFile', async () => {242const resource = joinPath(fixture.root, 'to-delete.txt');243await service.writeFile(resource, VSBuffer.fromString('delete me'));244assert.strictEqual(await service.exists(resource), true);245246await service.del(resource);247assert.strictEqual(await service.exists(resource), false);248});249250test('provider events bubble through file service', async () => {251let changeCount = 0;252const resource = joinPath(fixture.root, 'events.txt');253disposables.add(service.onDidFilesChange(e => {254if (e.contains(resource, FileChangeType.UPDATED) || e.contains(resource, FileChangeType.ADDED) || e.contains(resource, FileChangeType.DELETED)) {255changeCount++;256}257}));258259await service.writeFile(resource, VSBuffer.fromString('1'));260await service.writeFile(resource, VSBuffer.fromString('2'));261await service.del(resource);262263await timeout(20); // provider fires changes async264assert.ok(changeCount > 0);265});266267test('setReadOnly toggles provider capabilities', async () => {268provider.setReadOnly(true);269assert.ok(provider.capabilities & FileSystemProviderCapabilities.Readonly);270271let error: unknown;272try {273await service.writeFile(joinPath(fixture.root, 'readonly.txt'), VSBuffer.fromString('fail'));274} catch (e) {275error = e;276}277278assert.ok(error);279280provider.setReadOnly(false);281await service.writeFile(joinPath(fixture.root, 'readonly.txt'), VSBuffer.fromString('ok'));282});283});284285async function createFixture(service: FileService): Promise<Fixture> {286const root = URI.from({ scheme: Schemas.inMemory, path: '/' });287288await service.createFolder(joinPath(root, 'examples'));289await service.createFolder(joinPath(root, 'other'));290await service.writeFile(joinPath(root, 'index.html'), VSBuffer.fromString('Index'));291await service.writeFile(joinPath(root, 'site.css'), VSBuffer.fromString('body { }'));292293await service.writeFile(joinPath(root, 'small.txt'), VSBuffer.fromString('Small File'));294await service.writeFile(joinPath(root, 'small_umlaut.txt'), VSBuffer.fromString('Small File with Ümlaut'));295296await service.createFolder(joinPath(root, 'deep'));297await service.writeFile(joinPath(root, 'deep', 'conway.js'), VSBuffer.fromString('console.log("conway");'));298await service.writeFile(joinPath(root, 'deep', 'a.txt'), VSBuffer.fromString('A'));299await service.writeFile(joinPath(root, 'deep', 'b.txt'), VSBuffer.fromString('B'));300await service.writeFile(joinPath(root, 'deep', 'c.txt'), VSBuffer.fromString('C'));301302await service.createFolder(joinPath(root, 'other', 'deep'));303await service.writeFile(joinPath(root, 'other', 'deep', '1.txt'), VSBuffer.fromString('1'));304await service.writeFile(joinPath(root, 'other', 'deep', '2.txt'), VSBuffer.fromString('2'));305await service.writeFile(joinPath(root, 'other', 'deep', '3.txt'), VSBuffer.fromString('3'));306await service.writeFile(joinPath(root, 'other', 'deep', '4.txt'), VSBuffer.fromString('4'));307308return {309root,310indexHtml: joinPath(root, 'index.html'),311siteCss: joinPath(root, 'site.css'),312smallTxt: joinPath(root, 'small.txt'),313smallUmlautTxt: joinPath(root, 'small_umlaut.txt'),314deepDir: joinPath(root, 'deep'),315otherDeepDir: joinPath(root, 'other', 'deep')316};317}318319320