-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial Positron extension API integration tests (#3615)
This PR adds a single rather basic test for the Positron extension API: registering and then disposing a language runtime manager. (I noticed that there may actually be a bug in the disposing part, see the TODO in `runtime.test.ts`.) We can decide whether we're happy with this approach before investing too much in writing these tests. The test will run as part of the existing `vscode-api-tests` extension tests which are already a part of the `test-integration.sh` script. For convenience, I've also added a `test-positron-api.sh` script that _only_ runs Positron API tests, so you should be able to do: ```sh ./scripts/test-positron-api.sh ``` from the root directory. We'll likely need to fill out `TestLanguageRuntimeSession` as we add more tests. I suspect it may end up looking a lot like the Zed and JavaScript runtime session classes. Co-authored-by: seem <[email protected]>
- Loading branch information
Showing
4 changed files
with
233 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
164 changes: 164 additions & 0 deletions
164
extensions/vscode-api-tests/src/singlefolder-tests/positron/runtime.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<positron.LanguageRuntimeMessage>(); | ||
private readonly _onDidChangeRuntimeState = new vscode.EventEmitter<positron.RuntimeState>(); | ||
private readonly _onDidEndSession = new vscode.EventEmitter<positron.LanguageRuntimeExit>(); | ||
|
||
onDidReceiveRuntimeMessage: vscode.Event<positron.LanguageRuntimeMessage> = this._onDidReceiveRuntimeMessage.event; | ||
onDidChangeRuntimeState: vscode.Event<positron.RuntimeState> = this._onDidChangeRuntimeState.event; | ||
onDidEndSession: vscode.Event<positron.LanguageRuntimeExit> = 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<positron.RuntimeCodeFragmentStatus> { | ||
throw new Error('Not implemented.'); | ||
} | ||
|
||
async createClient(_id: string, _type: positron.RuntimeClientType, _params: any, _metadata?: any): Promise<void> { | ||
throw new Error('Not implemented.'); | ||
} | ||
|
||
async listClients(_type?: positron.RuntimeClientType | undefined): Promise<Record<string, string>> { | ||
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<positron.LanguageRuntimeInfo> { | ||
throw new Error('Not implemented.'); | ||
} | ||
|
||
async interrupt(): Promise<void> { | ||
throw new Error('Not implemented.'); | ||
} | ||
|
||
async restart(): Promise<void> { | ||
throw new Error('Not implemented.'); | ||
} | ||
|
||
async shutdown(_exitReason: positron.RuntimeExitReason): Promise<void> { | ||
throw new Error('Not implemented.'); | ||
} | ||
|
||
async forceQuit(): Promise<void> { | ||
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<positron.LanguageRuntimeMetadata>(); | ||
|
||
onDidDiscoverRuntime = this.onDidDiscoverRuntimeEmitter.event; | ||
|
||
async* discoverRuntimes(): AsyncGenerator<positron.LanguageRuntimeMetadata> { | ||
yield testLanguageRuntimeMetadata(); | ||
} | ||
|
||
async createSession( | ||
runtimeMetadata: positron.LanguageRuntimeMetadata, | ||
sessionMetadata: positron.RuntimeSessionMetadata | ||
): Promise<positron.LanguageRuntimeSession> { | ||
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', | ||
// ); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |