diff --git a/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts b/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts index c9cd240df2b..6798e6ee131 100644 --- a/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts +++ b/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts @@ -898,6 +898,10 @@ class ExtHostLanguageRuntimeSessionAdapter implements ILanguageRuntimeSession { } static clientCounter = 0; + + dispose(): void { + // Do nothing. + } } /** diff --git a/src/vs/workbench/contrib/positronIPyWidgets/test/browser/positronIPyWidgetsService.test.ts b/src/vs/workbench/contrib/positronIPyWidgets/test/browser/positronIPyWidgetsService.test.ts index 2fa797bc04f..f78b78a8d57 100644 --- a/src/vs/workbench/contrib/positronIPyWidgets/test/browser/positronIPyWidgetsService.test.ts +++ b/src/vs/workbench/contrib/positronIPyWidgets/test/browser/positronIPyWidgetsService.test.ts @@ -7,51 +7,23 @@ import { timeout } from 'vs/base/common/async'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { ILogService, NullLogService } from 'vs/platform/log/common/log'; -import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ILogService } from 'vs/platform/log/common/log'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; -import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; -import { NotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/browser/services/notebookRendererMessagingServiceImpl'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { INotebookRendererInfo, INotebookStaticPreloadInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; -import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IPyWidgetsInstance, PositronIPyWidgetsService } from 'vs/workbench/contrib/positronIPyWidgets/browser/positronIPyWidgetsService'; -import { IPositronNotebookOutputWebviewService } from 'vs/workbench/contrib/positronOutputWebview/browser/notebookOutputWebviewService'; -import { PositronNotebookOutputWebviewService } from 'vs/workbench/contrib/positronOutputWebview/browser/notebookOutputWebviewServiceImpl'; import { WebviewPlotClient } from 'vs/workbench/contrib/positronPlots/browser/webviewPlotClient'; -import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService'; import { RuntimeClientState } from 'vs/workbench/services/languageRuntime/common/languageRuntimeClientInstance'; -import { ILanguageRuntimeMessageClearOutput, ILanguageRuntimeMessageError, ILanguageRuntimeMessageOutput, ILanguageRuntimeMessageResult, ILanguageRuntimeMessageStream, LanguageRuntimeMessageType, LanguageRuntimeSessionMode, RuntimeOutputKind, RuntimeState } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; +import { ILanguageRuntimeMessageClearOutput, ILanguageRuntimeMessageError, ILanguageRuntimeMessageOutput, ILanguageRuntimeMessageResult, ILanguageRuntimeMessageStream, LanguageRuntimeMessageType, LanguageRuntimeSessionMode, RuntimeOutputKind } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; import { ToWebviewMessage } from 'vs/workbench/services/languageRuntime/common/positronIPyWidgetsWebviewMessages'; import { TestIPyWidgetsWebviewMessaging } from 'vs/workbench/services/languageRuntime/test/common/testIPyWidgetsWebviewMessaging'; -import { INotebookDocumentService, NotebookDocumentWorkbenchService } from 'vs/workbench/services/notebook/common/notebookDocumentService'; -import { IRuntimeSessionService, RuntimeClientType } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; +import { RuntimeClientType } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; import { TestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession'; -import { TestRuntimeSessionService } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService'; -import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; - -export class TestNotebookService implements Partial { - getRenderers(): INotebookRendererInfo[] { - return []; - } - - getPreferredRenderer(_mimeType: string): NotebookOutputRendererInfo | undefined { - return { - id: 'positron-ipywidgets', - extensionId: new ExtensionIdentifier('vscode.positron-ipywidgets'), - }; - } - - *getStaticPreloads(_viewType: string): Iterable { - // Yield nothing. - } -} +import { startTestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService'; +import { PositronTestServiceAccessor, positronWorkbenchInstantiationService } from 'vs/workbench/test/browser/positronWorkbenchTestServices'; +import { TestNotebookService } from 'vs/workbench/test/common/positronWorkbenchTestServices'; interface TestNotebookEditor extends INotebookEditor { changeModel(uri: URI): void; @@ -60,32 +32,17 @@ interface TestNotebookEditor extends INotebookEditor { suite('Positron - PositronIPyWidgetsService', () => { const disposables = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let positronIpywidgetsService: PositronIPyWidgetsService; - let runtimeSessionService: TestRuntimeSessionService; let notebookEditorService: INotebookEditorService; setup(() => { - const instantiationService = workbenchInstantiationService(undefined, disposables); - instantiationService.stub(INotebookRendererMessagingService, disposables.add(instantiationService.createInstance(NotebookRendererMessagingService))); - notebookEditorService = disposables.add(instantiationService.createInstance(NotebookEditorWidgetService)); - instantiationService.stub(INotebookEditorService, notebookEditorService); - instantiationService.stub(IWorkbenchThemeService, new TestThemeService() as any); - instantiationService.stub(INotebookDocumentService, new NotebookDocumentWorkbenchService()); - instantiationService.stub(INotebookService, new TestNotebookService()); - instantiationService.stub(IWebviewService, disposables.add(new WebviewService(instantiationService))); - instantiationService.stub(IPositronNotebookOutputWebviewService, instantiationService.createInstance(PositronNotebookOutputWebviewService)); - runtimeSessionService = disposables.add(new TestRuntimeSessionService()); - instantiationService.stub(IRuntimeSessionService, runtimeSessionService); - positronIpywidgetsService = disposables.add(instantiationService.createInstance(PositronIPyWidgetsService)); + instantiationService = positronWorkbenchInstantiationService(disposables); + const accessor = instantiationService.createInstance(PositronTestServiceAccessor); + notebookEditorService = accessor.notebookEditorService; + positronIpywidgetsService = accessor.positronIPyWidgetsService; }); - async function startConsoleSession() { - const session = disposables.add(new TestLanguageRuntimeSession(LanguageRuntimeSessionMode.Console)); - runtimeSessionService.startSession(session); - await timeout(0); - return session; - } - async function receiveIPyWidgetsResultMessage( session: TestLanguageRuntimeSession, parentId?: string, @@ -110,7 +67,7 @@ suite('Positron - PositronIPyWidgetsService', () => { disposables.add(positronIpywidgetsService.onDidCreatePlot(client => plotClient = client)); // Start a console session. - const session = await startConsoleSession(); + const session = await startTestLanguageRuntimeSession(instantiationService, disposables); // Simulate the runtime sending an IPyWidgets output message. const message = await receiveIPyWidgetsResultMessage(session); @@ -184,12 +141,11 @@ suite('Positron - PositronIPyWidgetsService', () => { notebookEditorService.addNotebookEditor(notebookEditor); // Start a notebook session. - const session = disposables.add(new TestLanguageRuntimeSession( - LanguageRuntimeSessionMode.Notebook, - notebookUri, - )); - runtimeSessionService.startSession(session); - await timeout(0); + const session = await startTestLanguageRuntimeSession( + instantiationService, + disposables, + { sessionMode: LanguageRuntimeSessionMode.Notebook, notebookUri }, + ); // Check that an instance was created. assert(positronIpywidgetsService.hasInstance(session.sessionId)); @@ -247,13 +203,12 @@ suite('Positron - IPyWidgetsInstance constructor', () => { let notebookService: INotebookService; setup(async () => { - logService = new NullLogService(); - session = disposables.add(new TestLanguageRuntimeSession()); + const instantiationService = positronWorkbenchInstantiationService(disposables); + const accessor = instantiationService.createInstance(PositronTestServiceAccessor); + logService = accessor.logService; + session = await startTestLanguageRuntimeSession(instantiationService, disposables); messaging = disposables.add(new TestIPyWidgetsWebviewMessaging()); notebookService = new TestNotebookService() as INotebookService; - - // Set the runtime state to ready. - session.setRuntimeState(RuntimeState.Ready); }); async function createIPyWidgetsInstance() { @@ -299,15 +254,16 @@ suite('Positron - IPyWidgetsInstance', () => { let ipywidgetsInstance: IPyWidgetsInstance; setup(async () => { - const logService = new NullLogService(); - session = disposables.add(new TestLanguageRuntimeSession()); + const instantiationService = positronWorkbenchInstantiationService(disposables); + const accessor = instantiationService.createInstance(PositronTestServiceAccessor); + session = await startTestLanguageRuntimeSession(instantiationService, disposables); messaging = disposables.add(new TestIPyWidgetsWebviewMessaging()); const notebookService = new TestNotebookService() as INotebookService; ipywidgetsInstance = disposables.add(new IPyWidgetsInstance( session, messaging, notebookService, - logService, + accessor.logService, )); // Clear initial messages. diff --git a/src/vs/workbench/contrib/positronPlots/test/electron-sandbox/positronPlotsService.test.ts b/src/vs/workbench/contrib/positronPlots/test/electron-sandbox/positronPlotsService.test.ts index 9bd93d8e56f..ac4cef58161 100644 --- a/src/vs/workbench/contrib/positronPlots/test/electron-sandbox/positronPlotsService.test.ts +++ b/src/vs/workbench/contrib/positronPlots/test/electron-sandbox/positronPlotsService.test.ts @@ -4,44 +4,30 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { raceTimeout, timeout } from 'vs/base/common/async'; +import { raceTimeout } from 'vs/base/common/async'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { PositronIPyWidgetsService } from 'vs/workbench/contrib/positronIPyWidgets/browser/positronIPyWidgetsService'; -import { PositronPlotsService } from 'vs/workbench/contrib/positronPlots/browser/positronPlotsService'; -import { PositronWebviewPreloadService } from 'vs/workbench/contrib/positronWebviewPreloads/browser/positronWebviewPreloadsService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { PositronTestServiceAccessor, positronWorkbenchInstantiationService as positronWorkbenchInstantiationService } from 'vs/workbench/test/browser/positronWorkbenchTestServices'; import { IPositronPlotMetadata } from 'vs/workbench/services/languageRuntime/common/languageRuntimePlotClient'; -import { LanguageRuntimeSessionMode } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; -import { IPositronIPyWidgetsService } from 'vs/workbench/services/positronIPyWidgets/common/positronIPyWidgetsService'; -import { HistoryPolicy, IPositronPlotClient } from 'vs/workbench/services/positronPlots/common/positronPlots'; -import { IPositronWebviewPreloadService } from 'vs/workbench/services/positronWebviewPreloads/common/positronWebviewPreloadService'; -import { IRuntimeSessionService, RuntimeClientType } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; +import { HistoryPolicy, IPositronPlotClient, IPositronPlotsService } from 'vs/workbench/services/positronPlots/common/positronPlots'; +import { RuntimeClientType } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; import { TestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession'; -import { TestRuntimeSessionService } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService'; -import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; -import { TestViewsService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { startTestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService'; suite('Positron - Plots Service', () => { const disposables = ensureNoDisposablesAreLeakedInTestSuite(); - let plotsService: PositronPlotsService; - let runtimeSessionService: TestRuntimeSessionService; + let instantiationService: TestInstantiationService; + let plotsService: IPositronPlotsService; setup(() => { - const instantiationService = workbenchInstantiationService(undefined, disposables); - runtimeSessionService = disposables.add(instantiationService.createInstance(TestRuntimeSessionService)); - instantiationService.stub(IRuntimeSessionService, runtimeSessionService); - instantiationService.stub(IPositronWebviewPreloadService, disposables.add(instantiationService.createInstance(PositronWebviewPreloadService))); - instantiationService.stub(IPositronIPyWidgetsService, disposables.add(instantiationService.createInstance(PositronIPyWidgetsService))); - instantiationService.stub(IViewsService, new TestViewsService()); - - plotsService = disposables.add(instantiationService.createInstance(PositronPlotsService)); + instantiationService = positronWorkbenchInstantiationService(disposables); + const accessor = instantiationService.createInstance(PositronTestServiceAccessor); + plotsService = accessor.positronPlotsService; }); async function createSession() { - const session = disposables.add(new TestLanguageRuntimeSession(LanguageRuntimeSessionMode.Console)); - runtimeSessionService.startSession(session); - - await timeout(0); + const session = await startTestLanguageRuntimeSession(instantiationService, disposables); const out: { session: TestLanguageRuntimeSession; diff --git a/src/vs/workbench/contrib/positronWebviewPreloads/test/browser/positronWebviewPreloadService.test.ts b/src/vs/workbench/contrib/positronWebviewPreloads/test/browser/positronWebviewPreloadService.test.ts index 0fd7deb1077..7c346222818 100644 --- a/src/vs/workbench/contrib/positronWebviewPreloads/test/browser/positronWebviewPreloadService.test.ts +++ b/src/vs/workbench/contrib/positronWebviewPreloads/test/browser/positronWebviewPreloadService.test.ts @@ -5,21 +5,13 @@ import assert from 'assert'; import { timeout } from 'vs/base/common/async'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { NotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/browser/services/notebookRendererMessagingServiceImpl'; -import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; -import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { PositronWebviewPreloadService } from 'vs/workbench/contrib/positronWebviewPreloads/browser/positronWebviewPreloadsService'; -import { TestNotebookService } from 'vs/workbench/contrib/positronIPyWidgets/test/browser/positronIPyWidgetsService.test'; -import { IPositronNotebookOutputWebviewService } from 'vs/workbench/contrib/positronOutputWebview/browser/notebookOutputWebviewService'; -import { PositronNotebookOutputWebviewService } from 'vs/workbench/contrib/positronOutputWebview/browser/notebookOutputWebviewServiceImpl'; +import { PositronTestServiceAccessor, positronWorkbenchInstantiationService } from 'vs/workbench/test/browser/positronWorkbenchTestServices'; import { WebviewPlotClient } from 'vs/workbench/contrib/positronPlots/browser/webviewPlotClient'; -import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService'; -import { LanguageRuntimeSessionMode, RuntimeOutputKind } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; -import { IRuntimeSessionService } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; +import { RuntimeOutputKind } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; import { TestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession'; -import { TestRuntimeSessionService } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { startTestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; const hvPreloadMessage = { @@ -56,27 +48,19 @@ const bokehDisplayMessage = { suite('Positron - PositronWebviewPreloadService', () => { const disposables = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let positronWebviewPreloadService: PositronWebviewPreloadService; - let runtimeSessionService: TestRuntimeSessionService; setup(() => { - const instantiationService = workbenchInstantiationService(undefined, disposables); - instantiationService.stub(INotebookRendererMessagingService, disposables.add(instantiationService.createInstance(NotebookRendererMessagingService))); - instantiationService.stub(INotebookService, new TestNotebookService()); - instantiationService.stub(IWebviewService, disposables.add(new WebviewService(instantiationService))); - instantiationService.stub(IPositronNotebookOutputWebviewService, instantiationService.createInstance(PositronNotebookOutputWebviewService)); - runtimeSessionService = disposables.add(new TestRuntimeSessionService()); - instantiationService.stub(IRuntimeSessionService, runtimeSessionService); - positronWebviewPreloadService = disposables.add(instantiationService.createInstance(PositronWebviewPreloadService)); + instantiationService = positronWorkbenchInstantiationService(disposables); + const accessor = instantiationService.createInstance(PositronTestServiceAccessor); + positronWebviewPreloadService = accessor.positronWebviewPreloadService; }); async function createConsoleSession() { // Start a console session. - const session = disposables.add(new TestLanguageRuntimeSession(LanguageRuntimeSessionMode.Console)); - runtimeSessionService.startSession(session); - - await timeout(0); + const session = await startTestLanguageRuntimeSession(instantiationService, disposables); const out: { session: TestLanguageRuntimeSession; diff --git a/src/vs/workbench/services/languageRuntime/common/languageRuntime.ts b/src/vs/workbench/services/languageRuntime/common/languageRuntime.ts index e275ac8f9d8..9181d2405c9 100644 --- a/src/vs/workbench/services/languageRuntime/common/languageRuntime.ts +++ b/src/vs/workbench/services/languageRuntime/common/languageRuntime.ts @@ -81,7 +81,7 @@ export class LanguageRuntimeService extends Disposable implements ILanguageRunti registerRuntime(metadata: ILanguageRuntimeMetadata): IDisposable { // If the runtime has already been registered, return early. if (this._registeredRuntimesByRuntimeId.has(metadata.runtimeId)) { - return toDisposable(() => { }); + return this._register(toDisposable(() => { })); } // Add the runtime to the registered runtimes. @@ -93,9 +93,9 @@ export class LanguageRuntimeService extends Disposable implements ILanguageRunti // Logging. this._logService.trace(`Language runtime ${formatLanguageRuntimeMetadata(metadata)} successfully registered.`); - return toDisposable(() => { + return this._register(toDisposable(() => { this._registeredRuntimesByRuntimeId.delete(metadata.runtimeId); - }); + })); } /** diff --git a/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts b/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts index 17b18ffe506..d9fd4ca94a6 100644 --- a/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts +++ b/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts @@ -109,7 +109,7 @@ export class UiClientInstance extends Disposable { super(); this._register(this._client); - this._comm = new PositronUiCommInstance(this._client); + this._comm = this._register(new PositronUiCommInstance(this._client)); this.onDidBusy = this._comm.onDidBusy; this.onDidClearConsole = this._comm.onDidClearConsole; this.onDidSetEditorSelections = this._comm.onDidSetEditorSelections; diff --git a/src/vs/workbench/services/runtimeSession/common/runtimeSessionService.ts b/src/vs/workbench/services/runtimeSession/common/runtimeSessionService.ts index 9cbeec703c6..2fe3a413bbf 100644 --- a/src/vs/workbench/services/runtimeSession/common/runtimeSessionService.ts +++ b/src/vs/workbench/services/runtimeSession/common/runtimeSessionService.ts @@ -74,7 +74,7 @@ export interface IRuntimeSessionMetadata { * The main interface for interacting with a language runtime session. */ -export interface ILanguageRuntimeSession { +export interface ILanguageRuntimeSession extends IDisposable { /** The language runtime's static metadata */ readonly runtimeMetadata: ILanguageRuntimeMetadata; diff --git a/src/vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession.ts b/src/vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession.ts index 43d3a2d0c74..22e8f89ac4b 100644 --- a/src/vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession.ts +++ b/src/vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession.ts @@ -7,9 +7,8 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILanguageRuntimeSession, IRuntimeClientInstance, IRuntimeSessionMetadata, RuntimeClientType } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; -import { ILanguageRuntimeClientCreatedEvent, ILanguageRuntimeExit, ILanguageRuntimeInfo, ILanguageRuntimeMessage, ILanguageRuntimeMessageClearOutput, ILanguageRuntimeMessageError, ILanguageRuntimeMessageInput, ILanguageRuntimeMessageIPyWidget, ILanguageRuntimeMessageOutput, ILanguageRuntimeMessagePrompt, ILanguageRuntimeMessageResult, ILanguageRuntimeMessageState, ILanguageRuntimeMessageStream, ILanguageRuntimeMetadata, ILanguageRuntimeStartupFailure, LanguageRuntimeMessageType, LanguageRuntimeSessionLocation, LanguageRuntimeSessionMode, LanguageRuntimeStartupBehavior, RuntimeCodeExecutionMode, RuntimeCodeFragmentStatus, RuntimeErrorBehavior, RuntimeExitReason, RuntimeOnlineState, RuntimeOutputKind, RuntimeState } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; +import { ILanguageRuntimeClientCreatedEvent, ILanguageRuntimeExit, ILanguageRuntimeInfo, ILanguageRuntimeMessage, ILanguageRuntimeMessageClearOutput, ILanguageRuntimeMessageError, ILanguageRuntimeMessageInput, ILanguageRuntimeMessageIPyWidget, ILanguageRuntimeMessageOutput, ILanguageRuntimeMessagePrompt, ILanguageRuntimeMessageResult, ILanguageRuntimeMessageState, ILanguageRuntimeMessageStream, ILanguageRuntimeMetadata, ILanguageRuntimeStartupFailure, LanguageRuntimeMessageType, RuntimeCodeExecutionMode, RuntimeCodeFragmentStatus, RuntimeErrorBehavior, RuntimeExitReason, RuntimeOnlineState, RuntimeOutputKind, RuntimeState } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; import { IRuntimeClientEvent } from 'vs/workbench/services/languageRuntime/common/languageRuntimeUiClient'; import { TestRuntimeClientInstance } from 'vs/workbench/services/languageRuntime/test/common/testRuntimeClientInstance'; @@ -63,47 +62,20 @@ export class TestLanguageRuntimeSession extends Disposable implements ILanguageR busy: false, }; - private readonly _languageVersion = '0.0.1'; - readonly runtimeMetadata: ILanguageRuntimeMetadata = { - base64EncodedIconSvg: '', - extensionId: new ExtensionIdentifier('test-extension'), - extraRuntimeData: {}, - languageId: 'test', - languageName: 'Test', - languageVersion: this._languageVersion, - runtimeId: '00000000-0000-0000-0000-100000000000', - runtimeName: `Test ${this._languageVersion}`, - runtimePath: '/test', - runtimeShortName: this._languageVersion, - runtimeSource: 'Test', - runtimeVersion: '0.0.1', - sessionLocation: LanguageRuntimeSessionLocation.Browser, - startupBehavior: LanguageRuntimeStartupBehavior.Implicit, - }; - - readonly metadata: IRuntimeSessionMetadata; - readonly sessionId: string; clientInstances = new Array>(); constructor( - sessionMode: LanguageRuntimeSessionMode = LanguageRuntimeSessionMode.Console, - notebookUri?: URI, + readonly metadata: IRuntimeSessionMetadata, + readonly runtimeMetadata: ILanguageRuntimeMetadata, ) { super(); - this.sessionId = 'session-id'; - - this.metadata = { - createdTimestamp: Date.now(), - sessionId: this.sessionId, - sessionMode, - sessionName: 'session-name', - startReason: 'test', - notebookUri, - }; + this.sessionId = this.metadata.sessionId; + // Track the runtime state. + this._register(this.onDidChangeRuntimeState(state => this._currentState = state)); } getRuntimeState(): RuntimeState { @@ -170,7 +142,18 @@ export class TestLanguageRuntimeSession extends Disposable implements ILanguageR } async start(): Promise { - throw new Error('Not implemented.'); + this._onDidChangeRuntimeState.fire(RuntimeState.Starting); + + // Complete the startup on the next tick, trying to match real runtime behavior. + setTimeout(() => { + this._onDidChangeRuntimeState.fire(RuntimeState.Ready); + }, 0); + + return { + banner: 'Test runtime started', + implementation_version: this.runtimeMetadata.runtimeVersion, + language_version: this.runtimeMetadata.languageVersion, + }; } async interrupt(): Promise { @@ -178,11 +161,27 @@ export class TestLanguageRuntimeSession extends Disposable implements ILanguageR } async restart(): Promise { - throw new Error('Not implemented.'); + await this.shutdown(RuntimeExitReason.Restart); + await this.start(); } - async shutdown(_exitReason: RuntimeExitReason): Promise { - throw new Error('Not implemented.'); + async shutdown(exitReason: RuntimeExitReason): Promise { + if (exitReason === RuntimeExitReason.Restart) { + this._onDidChangeRuntimeState.fire(RuntimeState.Restarting); + } else { + this._onDidChangeRuntimeState.fire(RuntimeState.Exiting); + } + + // Complete the shutdown on the next tick, trying to match real runtime behavior. + setTimeout(() => { + this._onDidChangeRuntimeState.fire(RuntimeState.Exited); + this._onDidEndSession.fire({ + runtime_name: this.runtimeMetadata.runtimeName, + exit_code: 0, + reason: exitReason, + message: '', + }); + }, 0); } async forceQuit(): Promise { @@ -204,7 +203,6 @@ export class TestLanguageRuntimeSession extends Disposable implements ILanguageR // Test helpers setRuntimeState(state: RuntimeState) { - this._currentState = state; this._onDidChangeRuntimeState.fire(state); } diff --git a/src/vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService.ts b/src/vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService.ts index 18e19582246..9ffc0958465 100644 --- a/src/vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService.ts +++ b/src/vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService.ts @@ -2,34 +2,122 @@ * Copyright (C) 2024 Posit Software, PBC. All rights reserved. * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ILanguageRuntimeClientCreatedEvent } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; -import { ILanguageRuntimeGlobalEvent, ILanguageRuntimeSession, IRuntimeSessionService, IRuntimeSessionWillStartEvent } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { LanguageService } from 'vs/editor/common/services/languageService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { LanguageRuntimeService } from 'vs/workbench/services/languageRuntime/common/languageRuntime'; +import { ILanguageRuntimeMetadata, ILanguageRuntimeService, LanguageRuntimeSessionLocation, LanguageRuntimeSessionMode, LanguageRuntimeStartupBehavior } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; +import { IPositronModalDialogsService } from 'vs/workbench/services/positronModalDialogs/common/positronModalDialogs'; +import { RuntimeSessionService } from 'vs/workbench/services/runtimeSession/common/runtimeSession'; +import { IRuntimeSessionService } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; +import { TestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession'; +import { TestOpenerService, TestPositronModalDialogService, TestCommandService, TestRuntimeSessionManager } from 'vs/workbench/test/common/positronWorkbenchTestServices'; +import { TestExtensionService, TestStorageService, TestWorkspaceTrustManagementService } from 'vs/workbench/test/common/workbenchTestServices'; -export class TestRuntimeSessionService extends Disposable implements Partial { - private readonly _willStartEmitter = this._register(new Emitter()); - private readonly _didStartRuntime = this._register(new Emitter()); - private readonly _didReceiveRuntimeEvent = this._register(new Emitter()); - private readonly _didCreateClientInstance = this._register(new Emitter()); +export function createRuntimeServices( + instantiationService: TestInstantiationService, + disposables: Pick = new DisposableStore, +): TestInstantiationService { + instantiationService.stub(IOpenerService, new TestOpenerService()); + instantiationService.stub(ILanguageService, disposables.add(new LanguageService())); + instantiationService.stub(IExtensionService, new TestExtensionService()); + instantiationService.stub(IStorageService, disposables.add(new TestStorageService())); + instantiationService.stub(ILogService, new NullLogService()); + instantiationService.stub(IWorkspaceTrustManagementService, disposables.add(new TestWorkspaceTrustManagementService())); + instantiationService.stub(ILanguageRuntimeService, disposables.add(instantiationService.createInstance(LanguageRuntimeService))); + instantiationService.stub(IPositronModalDialogsService, new TestPositronModalDialogService()); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + instantiationService.stub(ICommandService, new TestCommandService(instantiationService)); + instantiationService.stub(IKeybindingService, new MockKeybindingService()); + instantiationService.stub(IRuntimeSessionService, disposables.add(instantiationService.createInstance(RuntimeSessionService))); + return instantiationService; +} + +export function createTestLanguageRuntimeMetadata( + instantiationService: TestInstantiationService, + disposables: Pick, +): ILanguageRuntimeMetadata { + const languageRuntimeService = instantiationService.get(ILanguageRuntimeService); + const runtimeSessionService = instantiationService.get(IRuntimeSessionService); - readonly activeSessions = new Array(); + // Register the test runtime. + const languageName = 'Test'; + const languageVersion = '0.0.1'; + const runtime = { + extensionId: new ExtensionIdentifier('test-extension'), + base64EncodedIconSvg: '', + extraRuntimeData: {}, + languageId: 'test', + languageName, + languageVersion, + runtimeId: generateUuid(), + runtimeName: `${languageName} ${languageVersion}`, + runtimePath: '/test', + runtimeShortName: languageVersion, + runtimeSource: 'Test', + runtimeVersion: '0.0.1', + sessionLocation: LanguageRuntimeSessionLocation.Browser, + startupBehavior: LanguageRuntimeStartupBehavior.Implicit, + }; + disposables.add(languageRuntimeService.registerRuntime(runtime)); - readonly onWillStartSession = this._willStartEmitter.event; + // Register the test runtime manager. + const manager = new TestRuntimeSessionManager(); + disposables.add(runtimeSessionService.registerSessionManager(manager)); + + return runtime; +} - readonly onDidStartRuntime = this._didStartRuntime.event; +export interface IStartTestLanguageRuntimeSessionOptions { + runtime?: ILanguageRuntimeMetadata; + sessionName?: string; + sessionMode?: LanguageRuntimeSessionMode; + notebookUri?: URI; + startReason?: string; +} - readonly onDidReceiveRuntimeEvent = this._didReceiveRuntimeEvent.event; +export async function startTestLanguageRuntimeSession( + instantiationService: TestInstantiationService, + disposables: Pick, + options?: IStartTestLanguageRuntimeSessionOptions, +): Promise { + // Get or create the runtime. + const runtime = options?.runtime ?? createTestLanguageRuntimeMetadata(instantiationService, disposables); - readonly onDidCreateClientInstance = this._didCreateClientInstance.event; + // Start the session. + const runtimeSessionService = instantiationService.get(IRuntimeSessionService); + const sessionId = await runtimeSessionService.startNewRuntimeSession( + runtime.runtimeId, + options?.sessionName ?? 'test-session', + options?.sessionMode ?? LanguageRuntimeSessionMode.Console, + options?.notebookUri, + options?.startReason ?? 'Test requested to start a runtime session', + ); - // Test helpers. + // Get the session. + const session = runtimeSessionService.getSession(sessionId); + if (!session) { + throw new Error(`Failed to get session with ID '${sessionId}' after starting it`); + } - startSession(session: ILanguageRuntimeSession): void { - this.activeSessions.push(session); - this._register(session.onDidCreateClientInstance(e => this._didCreateClientInstance.fire(e))); - this._willStartEmitter.fire({ session, isNew: true }); - this._didStartRuntime.fire(session); + if (!(session instanceof TestLanguageRuntimeSession)) { + throw new Error(`Session with ID '${sessionId}' is not a TestLanguageRuntimeSession`); } -} + disposables.add(session); + return session; +} diff --git a/src/vs/workbench/test/browser/positronWorkbenchTestServices.ts b/src/vs/workbench/test/browser/positronWorkbenchTestServices.ts new file mode 100644 index 00000000000..ba631ac4a32 --- /dev/null +++ b/src/vs/workbench/test/browser/positronWorkbenchTestServices.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ILogService } from 'vs/platform/log/common/log'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; +import { NotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/browser/services/notebookRendererMessagingServiceImpl'; +import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { PositronIPyWidgetsService } from 'vs/workbench/contrib/positronIPyWidgets/browser/positronIPyWidgetsService'; +import { IPositronNotebookOutputWebviewService } from 'vs/workbench/contrib/positronOutputWebview/browser/notebookOutputWebviewService'; +import { PositronNotebookOutputWebviewService } from 'vs/workbench/contrib/positronOutputWebview/browser/notebookOutputWebviewServiceImpl'; +import { PositronPlotsService } from 'vs/workbench/contrib/positronPlots/browser/positronPlotsService'; +import { PositronWebviewPreloadService } from 'vs/workbench/contrib/positronWebviewPreloads/browser/positronWebviewPreloadsService'; +import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService'; +import { INotebookDocumentService, NotebookDocumentWorkbenchService } from 'vs/workbench/services/notebook/common/notebookDocumentService'; +import { IPositronIPyWidgetsService } from 'vs/workbench/services/positronIPyWidgets/common/positronIPyWidgetsService'; +import { IPositronPlotsService } from 'vs/workbench/services/positronPlots/common/positronPlots'; +import { IPositronWebviewPreloadService } from 'vs/workbench/services/positronWebviewPreloads/common/positronWebviewPreloadService'; +import { createRuntimeServices } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; +import { workbenchInstantiationService as baseWorkbenchInstantiationService, TestViewsService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestNotebookService } from 'vs/workbench/test/common/positronWorkbenchTestServices'; + +export function positronWorkbenchInstantiationService( + disposables: Pick = new DisposableStore(), +): TestInstantiationService { + const instantiationService = baseWorkbenchInstantiationService(undefined, disposables); + + createRuntimeServices(instantiationService, disposables); + + instantiationService.stub(INotebookRendererMessagingService, disposables.add(instantiationService.createInstance(NotebookRendererMessagingService))); + instantiationService.stub(INotebookEditorService, disposables.add(instantiationService.createInstance(NotebookEditorWidgetService))); + instantiationService.stub(IWorkbenchThemeService, new TestThemeService() as any); + instantiationService.stub(INotebookDocumentService, new NotebookDocumentWorkbenchService()); + instantiationService.stub(INotebookService, new TestNotebookService()); + instantiationService.stub(IWebviewService, disposables.add(new WebviewService(instantiationService))); + instantiationService.stub(IPositronNotebookOutputWebviewService, instantiationService.createInstance(PositronNotebookOutputWebviewService)); + instantiationService.stub(IPositronIPyWidgetsService, disposables.add(instantiationService.createInstance(PositronIPyWidgetsService))); + instantiationService.stub(IPositronWebviewPreloadService, disposables.add(instantiationService.createInstance(PositronWebviewPreloadService))); + instantiationService.stub(IPositronIPyWidgetsService, disposables.add(instantiationService.createInstance(PositronIPyWidgetsService))); + instantiationService.stub(IViewsService, new TestViewsService()); + instantiationService.stub(IPositronPlotsService, disposables.add(instantiationService.createInstance(PositronPlotsService))); + + return instantiationService; +} + +export class PositronTestServiceAccessor { + constructor( + @ILogService public logService: ILogService, + @INotebookEditorService public notebookEditorService: INotebookEditorService, + @IPositronIPyWidgetsService public positronIPyWidgetsService: PositronIPyWidgetsService, + @IPositronPlotsService public positronPlotsService: IPositronPlotsService, + @IPositronWebviewPreloadService public positronWebviewPreloadService: PositronWebviewPreloadService, + ) { } +} diff --git a/src/vs/workbench/test/common/positronWorkbenchTestServices.ts b/src/vs/workbench/test/common/positronWorkbenchTestServices.ts new file mode 100644 index 00000000000..32923d7e7d3 --- /dev/null +++ b/src/vs/workbench/test/common/positronWorkbenchTestServices.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { ICommandService, ICommandEvent, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService, IOpener, IValidator, IExternalUriResolver, IExternalOpener, OpenInternalOptions, OpenExternalOptions, ResolveExternalUriOptions, IResolvedExternalUri } from 'vs/platform/opener/common/opener'; +import { INotebookRendererInfo, INotebookStaticPreloadInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { ILanguageRuntimeMetadata } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService'; +import { IPositronModalDialogsService, ShowConfirmationModalDialogOptions, IModalDialogPromptInstance } from 'vs/workbench/services/positronModalDialogs/common/positronModalDialogs'; +import { ILanguageRuntimeSessionManager, IRuntimeSessionMetadata, ILanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService'; +import { TestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession'; + +export class TestNotebookService implements Partial { + getRenderers(): INotebookRendererInfo[] { + return []; + } + + getPreferredRenderer(_mimeType: string): NotebookOutputRendererInfo | undefined { + return { + id: 'positron-ipywidgets', + extensionId: new ExtensionIdentifier('vscode.positron-ipywidgets'), + }; + } + + *getStaticPreloads(_viewType: string): Iterable { + // Yield nothing. + } +} + +export class TestOpenerService implements IOpenerService { + _serviceBrand: undefined; + registerOpener(opener: IOpener): IDisposable { + return { dispose() { } }; + } + registerValidator(validator: IValidator): IDisposable { + throw new Error('Method not implemented.'); + } + registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable { + throw new Error('Method not implemented.'); + } + setDefaultExternalOpener(opener: IExternalOpener): void { + throw new Error('Method not implemented.'); + } + registerExternalOpener(opener: IExternalOpener): IDisposable { + throw new Error('Method not implemented.'); + } + open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise { + throw new Error('Method not implemented.'); + } + resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise { + throw new Error('Method not implemented.'); + } +} +// Copied from src/vs/editor/test/browser/editorTestServices.ts for access outside of the browser context. +export class TestCommandService implements ICommandService { + declare readonly _serviceBrand: undefined; + + private readonly _instantiationService: IInstantiationService; + + private readonly _onWillExecuteCommand = new Emitter(); + public readonly onWillExecuteCommand: Event = this._onWillExecuteCommand.event; + + private readonly _onDidExecuteCommand = new Emitter(); + public readonly onDidExecuteCommand: Event = this._onDidExecuteCommand.event; + + constructor(instantiationService: IInstantiationService) { + this._instantiationService = instantiationService; + } + + public executeCommand(id: string, ...args: any[]): Promise { + const command = CommandsRegistry.getCommand(id); + if (!command) { + return Promise.reject(new Error(`command '${id}' not found`)); + } + + try { + this._onWillExecuteCommand.fire({ commandId: id, args }); + const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler, ...args]) as T; + this._onDidExecuteCommand.fire({ commandId: id, args }); + return Promise.resolve(result); + } catch (err) { + return Promise.reject(err); + } + } +} +export class TestPositronModalDialogService implements IPositronModalDialogsService { + _serviceBrand: undefined; + showConfirmationModalDialog(options: ShowConfirmationModalDialogOptions): void { + throw new Error('Method not implemented.'); + } + showModalDialogPrompt(title: string, message: string, okButtonTitle?: string, cancelButtonTitle?: string): IModalDialogPromptInstance { + throw new Error('Method not implemented.'); + } + showSimpleModalDialogPrompt(title: string, message: string, okButtonTitle?: string, cancelButtonTitle?: string): Promise { + throw new Error('Method not implemented.'); + } + showSimpleModalDialogMessage(title: string, message: string, okButtonTitle?: string): Promise { + throw new Error('Method not implemented.'); + } +} +export class TestRuntimeSessionManager implements ILanguageRuntimeSessionManager { + async managesRuntime(runtime: ILanguageRuntimeMetadata): Promise { + return true; + } + + async createSession(runtimeMetadata: ILanguageRuntimeMetadata, sessionMetadata: IRuntimeSessionMetadata): Promise { + return new TestLanguageRuntimeSession(sessionMetadata, runtimeMetadata); + } + + async restoreSession(runtimeMetadata: ILanguageRuntimeMetadata, sessionMetadata: IRuntimeSessionMetadata): Promise { + return new TestLanguageRuntimeSession(sessionMetadata, runtimeMetadata); + } + + validateMetadata(metadata: ILanguageRuntimeMetadata): Promise { + throw new Error('Method not implemented'); + } +}