From b6270f8f56adefba4aced29e774116801f049db2 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Thu, 19 Dec 2024 03:35:12 +0000 Subject: [PATCH] [refactor]: working on unit tests for the `FilePromptContentsProvider` --- src/vs/base/common/decorators.ts | 2 + .../test/common/codecs/linesDecoder.test.ts | 6 ++ .../filePromptContentsProvider.ts | 2 +- .../textModelContentsProvider.ts | 3 + .../filePromptContentsProvider.test.ts | 95 +++++++++++++++++++ 5 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/vs/workbench/contrib/chat/test/common/promptContentProviders/filePromptContentsProvider.test.ts diff --git a/src/vs/base/common/decorators.ts b/src/vs/base/common/decorators.ts index 9a40158d9e6..0f02b3ede2c 100644 --- a/src/vs/base/common/decorators.ts +++ b/src/vs/base/common/decorators.ts @@ -127,3 +127,5 @@ export function throttle(delay: number, reducer?: IDebounceReducer, initia }; }); } + +export { cancelPreviousCalls } from './decorators/cancelPreviousCalls.js'; diff --git a/src/vs/editor/test/common/codecs/linesDecoder.test.ts b/src/vs/editor/test/common/codecs/linesDecoder.test.ts index 6ff62a74b2d..caf77689ce2 100644 --- a/src/vs/editor/test/common/codecs/linesDecoder.test.ts +++ b/src/vs/editor/test/common/codecs/linesDecoder.test.ts @@ -9,6 +9,7 @@ import { Range } from '../../../common/core/range.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { newWriteableStream } from '../../../../base/common/stream.js'; import { Line } from '../../../common/codecs/linesCodec/tokens/line.js'; +import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; import { NewLine } from '../../../common/codecs/linesCodec/tokens/newLine.js'; import { CarriageReturn } from '../../../common/codecs/linesCodec/tokens/carriageReturn.js'; import { LinesDecoder, TLineToken } from '../../../common/codecs/linesCodec/linesDecoder.js'; @@ -159,6 +160,11 @@ suite('LinesDecoder', () => { "Must return an instance of the original decoder.", ); + assert( + transformed instanceof BaseDecoder, + "Must return an instance of the base decoder.", + ); + setTimeout(() => { stream.write(VSBuffer.fromString('hello\nworld\r\nhow ☁️ are\n\nyou\rdoing\n\n')); diff --git a/src/vs/workbench/contrib/chat/common/promptContentProviders/filePromptContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptContentProviders/filePromptContentsProvider.ts index cd818783599..7f5ebaf635e 100644 --- a/src/vs/workbench/contrib/chat/common/promptContentProviders/filePromptContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptContentProviders/filePromptContentsProvider.ts @@ -34,7 +34,7 @@ export class FilePromptContentProvider extends PromptContentsProviderBase { + /** + * URI component of the prompt associated with this contents provider. + */ public readonly uri = this.model.uri; constructor( diff --git a/src/vs/workbench/contrib/chat/test/common/promptContentProviders/filePromptContentsProvider.test.ts b/src/vs/workbench/contrib/chat/test/common/promptContentProviders/filePromptContentsProvider.test.ts new file mode 100644 index 00000000000..c6922601654 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/promptContentProviders/filePromptContentsProvider.test.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { ReadableStream } from '../../../../../../base/common/stream.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { IFileService } from '../../../../../../platform/files/common/files.js'; +import { FileService } from '../../../../../../platform/files/common/fileService.js'; +import { NullPolicyService } from '../../../../../../platform/policy/common/policy.js'; +import { ILogService, NullLogService } from '../../../../../../platform/log/common/log.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { ConfigurationService } from '../../../../../../platform/configuration/common/configurationService.js'; +import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { randomInt } from '../../../../../../base/common/numbers.js'; +import { FilePromptContentProvider } from '../../../common/promptContentProviders/filePromptContentsProvider.js'; +import { Line } from '../../../../../../editor/common/codecs/linesCodec/tokens/line.js'; +import { BaseDecoder } from '../../../../../../base/common/codecs/baseDecoder.js'; +import { InMemoryFileSystemProvider } from '../../../../../../platform/files/common/inMemoryFilesystemProvider.js'; +import { Schemas } from '../../../../../../base/common/network.js'; + +suite('FilePromptContentsProvider', function () { + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); + + let instantiationService: TestInstantiationService; + setup(async () => { + const nullPolicyService = new NullPolicyService(); + const nullLogService = testDisposables.add(new NullLogService()); + const nullFileService = testDisposables.add(new FileService(nullLogService)); + const nullConfigService = testDisposables.add(new ConfigurationService( + URI.file('/config.json'), + nullFileService, + nullPolicyService, + nullLogService, + )); + instantiationService = testDisposables.add(new TestInstantiationService()); + + const fileSystemProvider = testDisposables.add(new InMemoryFileSystemProvider()); + testDisposables.add(nullFileService.registerProvider(Schemas.file, fileSystemProvider)); + + instantiationService.stub(IFileService, nullFileService); + instantiationService.stub(ILogService, nullLogService); + instantiationService.stub(IConfigurationService, nullConfigService); + }); + + test('provides contents of a file', async function () { + const fileService = instantiationService.get(IFileService); + + const fileName = `file-${randomInt(10000)}.prompt.md`; + const fileUri = URI.file(`/${fileName}`); + + if (await fileService.exists(fileUri)) { + await fileService.del(fileUri); + } + await fileService.writeFile(fileUri, VSBuffer.fromString('Hello, world!')); + await new Promise((resolve) => setTimeout(resolve, 5)); + + const contentsProvider = testDisposables.add(instantiationService.createInstance( + FilePromptContentProvider, + fileUri, + )); + + let streamOrError: ReadableStream | Error | undefined; + testDisposables.add(contentsProvider.onContentChanged((event) => { + streamOrError = event; + })); + contentsProvider.start(); + + await new Promise((resolve) => setTimeout(resolve, 25)); + + assert( + streamOrError instanceof BaseDecoder, + `Provider must produce a stream of lines, got '${streamOrError}'.`, + ); + + const stream = testDisposables.add(streamOrError); + + const receivedLines = await stream.consumeAll(); + assert.strictEqual( + receivedLines.length, + 1, + 'Must read the correct number of lines from the provider.', + ); + + const expectedLine = new Line(1, 'Hello, world!'); + const receivedLine = receivedLines[0]; + assert( + receivedLine.equals(expectedLine), + `Expected to receive '${expectedLine}', got '${receivedLine}'.`, + ); + }); +});