Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ jobs:
node-version: 24
registry-url: https://registry.npmjs.org/

- name: Cache VS Code test downloads
uses: actions/cache@v4
with:
path: .vscode-test
key: vscode-test-${{ runner.os }}-stable

- name: Install X server
run: |
sudo apt-get update
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
modelica-language-server*.vsix
node_modules/
out/
*.tsbuildinfo
client/testFixture/.vscode/settings.json
7 changes: 3 additions & 4 deletions client/src/test/gotoDeclaration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,17 @@

import * as vscode from 'vscode';
import * as assert from 'assert';
import { getDocUri, activate } from './helper';
import { getDocUri, activate, executeProviderUntilResult } from './helper';

suite('Goto Declaration', () => {
test('onDeclaration()', async () => {
const docUri = getDocUri('MyLibrary.mo');
await activate(docUri);

const position = new vscode.Position(4, 18);
const actualLocations = await vscode.commands.executeCommand<vscode.LocationLink[]>(
const actualLocations = await executeProviderUntilResult<vscode.LocationLink[]>(
'vscode.executeDeclarationProvider',
docUri,
position,
[docUri, position],
);

assert.strictEqual(actualLocations.length, 1);
Expand Down
45 changes: 41 additions & 4 deletions client/src/test/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,19 @@ export let editor: vscode.TextEditor;
export let documentEol: string;
export let platformEol: string;


/**
* Activates the OpenModelica.modelica-language-server extension
* Activates the OpenModelica.modelica-language-server extension and opens the
* given document.
*
* This resolves once the extension is activated and the document is open, but
* NOT once the language server has finished initializing and parsing it (that
* happens asynchronously). Callers must therefore not assume the server is
* ready when this returns: run provider commands through
* {@link executeProviderUntilResult}, which polls until a non-empty result is
* available instead of relying on a fixed delay.
*
* @param docUri The document to open in the editor.
*/
export async function activate(docUri: vscode.Uri): Promise<void> {
// The extensionId is `publisher.name` from package.json
Expand All @@ -54,14 +65,40 @@ export async function activate(docUri: vscode.Uri): Promise<void> {
try {
doc = await vscode.workspace.openTextDocument(docUri);
editor = await vscode.window.showTextDocument(doc);
await sleep(5000); // Wait for server activation
// No fixed wait for server activation here: callers use
// `executeProviderUntilResult`, which polls until the server is ready.
} catch (e) {
console.error(e);
}
}

async function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
/**
* Repeatedly run a VS Code command until it returns a non-empty result.
*
* The language server initializes and parses documents asynchronously, so a
* single fixed delay after activation is racy: on slower machines (e.g. CI)
* the provider can still return an empty result. Polling avoids that flakiness
* while keeping fast machines fast.
*
* @param command Command id to execute, e.g. `vscode.executeDeclarationProvider`.
* @param args Arguments forwarded to the command.
* @param timeoutMs Maximum time to keep retrying.
* @param intervalMs Delay between attempts.
* @returns The first non-empty result, or the last (empty) result on timeout.
*/
export async function executeProviderUntilResult<T extends { length: number }>(
command: string,
args: unknown[],
timeoutMs = 20000,
intervalMs = 250,
): Promise<T> {
const deadline = Date.now() + timeoutMs;
let result = await vscode.commands.executeCommand<T>(command, ...args);
while ((!result || result.length === 0) && Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, intervalMs));
result = await vscode.commands.executeCommand<T>(command, ...args);
}
return result;
}

export const getDocPath = (p: string): string => {
Expand Down
7 changes: 3 additions & 4 deletions client/src/test/mslLibrary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

import * as vscode from 'vscode';
import * as assert from 'assert';
import { getDocUri, activate } from './helper';
import { getDocUri, activate, executeProviderUntilResult } from './helper';

suite('MSL Library Support', () => {
test('go-to-declaration resolves MSL type into MSL directory', async () => {
Expand All @@ -51,10 +51,9 @@ suite('MSL Library Support', () => {

// Line 2: " Modelica.Units.SI.Voltage v;" — cursor on "Modelica" at column 4
const position = new vscode.Position(2, 4);
const actualLocations = await vscode.commands.executeCommand<vscode.LocationLink[]>(
const actualLocations = await executeProviderUntilResult<vscode.LocationLink[]>(
'vscode.executeDeclarationProvider',
docUri,
position,
[docUri, position],
);

assert.ok(actualLocations.length > 0, 'Expected at least one declaration location');
Expand Down
1 change: 1 addition & 0 deletions client/src/test/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ async function main() {

// Download VS Code, unzip it and run the integration test
await runTests({
version: 'stable',
extensionDevelopmentPath,
extensionTestsPath,
launchArgs: [testFixturePath],
Expand Down
6 changes: 3 additions & 3 deletions client/src/test/symbolinformation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

import * as vscode from 'vscode';
import * as assert from 'assert';
import { getDocUri, activate } from './helper';
import { getDocUri, activate, executeProviderUntilResult } from './helper';

suite('Symbol Information', () => {
const docUri = getDocUri('MyLibrary.mo');
Expand Down Expand Up @@ -71,9 +71,9 @@ async function testSymbolInformation(
await activate(docUri);

// Execute `vscode.executeDocumentSymbolProvider` to get file outline
const actualSymbolInformation = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
const actualSymbolInformation = await executeProviderUntilResult<vscode.DocumentSymbol[]>(
'vscode.executeDocumentSymbolProvider',
docUri,
[docUri],
);

assertDocumentSymbolsEqual(expectedDocumentSymbols, actualSymbolInformation);
Expand Down
Loading