diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/index.ts b/extensions/vscode-api-tests/src/singlefolder-tests/index.ts index 6798fc5a1e0..3b31742c258 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/index.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/index.ts @@ -34,6 +34,12 @@ if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { } }; } +// --- Start Positron --- +// Allow running a subset of tests. +if (process.env.VSCODE_MOCHA_GREP) { + options.grep = process.env.VSCODE_MOCHA_GREP; +} +// --- End Positron --- testRunner.configure(options); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/positron/runtime.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/positron/runtime.test.ts new file mode 100644 index 00000000000..251ab629e5f --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/positron/runtime.test.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import * as positron from 'positron'; +import * as vscode from 'vscode'; +import { assertNoRpcFromEntry, disposeAll, poll } from '../../utils'; +import { Disposable } from 'vscode'; +import assert = require('assert'); + +class TestLanguageRuntimeSession implements positron.LanguageRuntimeSession { + private readonly _onDidReceiveRuntimeMessage = new vscode.EventEmitter(); + private readonly _onDidChangeRuntimeState = new vscode.EventEmitter(); + private readonly _onDidEndSession = new vscode.EventEmitter(); + + onDidReceiveRuntimeMessage: vscode.Event = this._onDidReceiveRuntimeMessage.event; + onDidChangeRuntimeState: vscode.Event = this._onDidChangeRuntimeState.event; + onDidEndSession: vscode.Event = this._onDidEndSession.event; + + readonly dynState = { + inputPrompt: `T>`, + continuationPrompt: 'T+', + }; + + constructor( + readonly runtimeMetadata: positron.LanguageRuntimeMetadata, + readonly metadata: positron.RuntimeSessionMetadata + ) { } + + execute(_code: string, _id: string, _mode: positron.RuntimeCodeExecutionMode, _errorBehavior: positron.RuntimeErrorBehavior): void { + throw new Error('Not implemented.'); + } + + async isCodeFragmentComplete(_code: string): Promise { + throw new Error('Not implemented.'); + } + + async createClient(_id: string, _type: positron.RuntimeClientType, _params: any, _metadata?: any): Promise { + throw new Error('Not implemented.'); + } + + async listClients(_type?: positron.RuntimeClientType | undefined): Promise> { + throw new Error('Not implemented.'); + } + + removeClient(_id: string): void { + throw new Error('Not implemented.'); + } + + sendClientMessage(_client_id: string, _message_id: string, _message: any): void { + throw new Error('Not implemented.'); + } + + replyToPrompt(_id: string, _reply: string): void { + throw new Error('Not implemented.'); + } + + async start(): Promise { + throw new Error('Not implemented.'); + } + + async interrupt(): Promise { + throw new Error('Not implemented.'); + } + + async restart(): Promise { + throw new Error('Not implemented.'); + } + + async shutdown(_exitReason: positron.RuntimeExitReason): Promise { + throw new Error('Not implemented.'); + } + + async forceQuit(): Promise { + throw new Error('Not implemented.'); + } + + dispose() { + } +} + +function testLanguageRuntimeMetadata(): positron.LanguageRuntimeMetadata { + const languageVersion = '0.0.1'; + const runtimeShortName = languageVersion; + return { + base64EncodedIconSvg: '', + extraRuntimeData: {}, + languageId: 'test', + languageName: 'Test', + languageVersion, + runtimeId: '00000000-0000-0000-0000-100000000000', + runtimeName: `Test ${runtimeShortName}`, + runtimePath: '/test', + runtimeShortName, + runtimeSource: 'Test', + runtimeVersion: '0.0.1', + sessionLocation: positron.LanguageRuntimeSessionLocation.Browser, + startupBehavior: positron.LanguageRuntimeStartupBehavior.Implicit, + }; +} + +class TestLanguageRuntimeManager implements positron.LanguageRuntimeManager { + readonly onDidDiscoverRuntimeEmitter = new vscode.EventEmitter(); + + onDidDiscoverRuntime = this.onDidDiscoverRuntimeEmitter.event; + + async* discoverRuntimes(): AsyncGenerator { + yield testLanguageRuntimeMetadata(); + } + + async createSession( + runtimeMetadata: positron.LanguageRuntimeMetadata, + sessionMetadata: positron.RuntimeSessionMetadata + ): Promise { + return new TestLanguageRuntimeSession(runtimeMetadata, sessionMetadata); + } +} + +suite('positron API - runtime', () => { + + let disposables: Disposable[]; + setup(() => { + disposables = []; + }); + + teardown(async function () { + assertNoRpcFromEntry([positron, 'positron']); + disposeAll(disposables); + }); + + test('register a runtime manager', async () => { + const getRegisteredRuntimes = async () => + (await positron.runtime.getRegisteredRuntimes()) + .filter(runtime => runtime.languageId === 'test'); + + assert.deepStrictEqual( + await getRegisteredRuntimes(), + [], + 'no test runtimes should be registered'); + + // Register a manager. + const manager = new TestLanguageRuntimeManager(); + const managerDisposable = positron.runtime.registerLanguageRuntimeManager(manager); + + // The manager's runtimes should eventually be registered. + await poll( + getRegisteredRuntimes, + (runtimes) => runtimes.length > 0, + 'runtimes should be registered', + ); + + managerDisposable.dispose(); + + // TODO: Unregistering a manager unregisters its runtimes, but doesn't remove them from + // the list returned by positron.runtime.getRegisteredRuntimes. Is that a bug? + // It also means that this test will currently fail if run out of order. + // await poll( + // getRegisteredRuntimes, + // (runtimes) => runtimes.length === 0, + // 'test runtimes should be unregistered', + // ); + }); + +}); diff --git a/extensions/vscode-api-tests/tsconfig.json b/extensions/vscode-api-tests/tsconfig.json index 3ef85d919ec..152f9bf03b1 100644 --- a/extensions/vscode-api-tests/tsconfig.json +++ b/extensions/vscode-api-tests/tsconfig.json @@ -9,6 +9,10 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + // --- Start Positron --- + // Add positron.d.ts. + "../../src/vscode-dts/vscode.proposed.*.d.ts", + "../../src/positron-dts/positron.d.ts", + // --- End Positron --- ] } diff --git a/scripts/test-positron-api.sh b/scripts/test-positron-api.sh new file mode 100755 index 00000000000..ce025a33ced --- /dev/null +++ b/scripts/test-positron-api.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# This script runs only the Positron API integration tests defined at extensions/vscode-api-tests/src. +# It's largely copied from scripts/test-integration.sh, but with all other tests removed. + +set -e + +if [[ "$OSTYPE" == "darwin"* ]]; then + realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } + ROOT=$(dirname $(dirname $(realpath "$0"))) +else + ROOT=$(dirname $(dirname $(readlink -f $0))) + # --disable-dev-shm-usage: when run on docker containers where size of /dev/shm + # partition < 64MB which causes OOM failure for chromium compositor that uses the partition for shared memory + LINUX_EXTRA_ARGS="--disable-dev-shm-usage" +fi + +VSCODEUSERDATADIR=`mktemp -d 2>/dev/null` +VSCODECRASHDIR=$ROOT/.build/crashes +VSCODELOGSDIR=$ROOT/.build/logs/integration-tests + +cd $ROOT + +# Figure out which Electron to use for running tests +if [ -z "$INTEGRATION_TEST_ELECTRON_PATH" ] +then + INTEGRATION_TEST_ELECTRON_PATH="./scripts/code.sh" + + echo "Running integration tests out of sources." +else + export VSCODE_CLI=1 + export ELECTRON_ENABLE_LOGGING=1 + + echo "Running integration tests with '$INTEGRATION_TEST_ELECTRON_PATH' as build." +fi + +echo "Storing crash reports into '$VSCODECRASHDIR'." +echo "Storing log files into '$VSCODELOGSDIR'." + + +# Tests in the extension host + +API_TESTS_EXTRA_ARGS="--disable-telemetry --skip-welcome --skip-release-notes --crash-reporter-directory=$VSCODECRASHDIR --logsPath=$VSCODELOGSDIR --no-cached-data --disable-updates --use-inmemory-secretstorage --disable-extensions --disable-workspace-trust --user-data-dir=$VSCODEUSERDATADIR" + +if [ -z "$INTEGRATION_TEST_APP_NAME" ]; then + kill_app() { true; } +else + kill_app() { killall $INTEGRATION_TEST_APP_NAME || true; } +fi + +echo +echo "### Positron API tests (folder)" +echo +VSCODE_MOCHA_GREP='positron API' "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests $API_TESTS_EXTRA_ARGS +kill_app + +# Cleanup + +rm -rf $VSCODEUSERDATADIR