Skip to content

Commit

Permalink
Add basic lsp integration test to check language support
Browse files Browse the repository at this point in the history
- Roll relevant test under language folder for better organization.
- Given how build all task for defaultPacakge will need to be executed
as a set up for this test, remove @slow for other tests that's running
this task as the cost will be atmortized in CI.
- The test check lsp client is running and exercise Goto Definition
Request and Find References Request.

Issue: swiftlang#1235
  • Loading branch information
michael-weng committed Dec 3, 2024
1 parent 3c116a4 commit 34c4d9c
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 20 deletions.
2 changes: 0 additions & 2 deletions src/BackgroundCompilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import { WorkspaceContext } from "./WorkspaceContext";
import { TaskOperation } from "./tasks/TaskQueue";

export class BackgroundCompilation {
private waitingToRun = false;

constructor(private folderContext: FolderContext) {}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

import * as assert from "assert";
import * as vscode from "vscode";
import { WorkspaceContext } from "../../src/WorkspaceContext";
import { testAssetUri } from "../fixtures";
import { waitForNoRunningTasks } from "../utilities";
import { Workbench } from "../../src/utilities/commands";
import { activateExtensionForTest, updateSettings } from "./utilities/testutilities";
import { WorkspaceContext } from "../../../src/WorkspaceContext";
import { testAssetUri } from "../../fixtures";
import { waitForNoRunningTasks } from "../../utilities";
import { Workbench } from "../../../src/utilities/commands";
import { activateExtensionForTest, updateSettings } from "../utilities/testutilities";

suite("BackgroundCompilation Test Suite", () => {
let workspaceContext: WorkspaceContext;
Expand All @@ -38,7 +38,7 @@ suite("BackgroundCompilation Test Suite", () => {
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
});

test("build all on save @slow", async () => {
test("build all on save", async () => {
const taskPromise = new Promise<void>(res => {
vscode.tasks.onDidStartTask(e => {
const task = e.execution.task;
Expand All @@ -58,5 +58,5 @@ suite("BackgroundCompilation Test Suite", () => {
await vscode.workspace.save(uri);

await taskPromise;
}).timeout(120000);
}).timeout(2 * 60 * 1000);
});
116 changes: 116 additions & 0 deletions test/integration-tests/language/GotoDefinition.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the VS Code Swift open source project
//
// Copyright (c) 2024 the VS Code Swift project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import * as vscode from "vscode";
import * as langclient from "vscode-languageclient/node";
import { expect } from "chai";
import { LanguageClientManager } from "../../../src/sourcekit-lsp/LanguageClientManager";
import { WorkspaceContext } from "../../../src/WorkspaceContext";
import { testAssetUri } from "../../fixtures";
import { FolderContext } from "../../../src/FolderContext";
import { executeTaskAndWaitForResult, waitForNoRunningTasks } from "../../utilities";
import { SwiftTask } from "../../../src/tasks/SwiftTaskProvider";
import { activateExtensionForSuite, folderInRootWorkspace } from "../utilities/testutilities";

async function waitForClientState(
languageClientManager: LanguageClientManager,
expectedState: langclient.State
): Promise<langclient.State> {
let clientState = undefined;
while (clientState !== expectedState) {
clientState = await languageClientManager.useLanguageClient(async client => client.state);
console.warn("Language client is not ready yet. Retrying in 100 ms...");
await new Promise(resolve => setTimeout(resolve, 100));
}
return clientState;
}

suite("Integration, Basic Language Support with Sourcekit-lsp", function () {
this.timeout(60 * 1000);

let clientManager: LanguageClientManager;
let workspaceContext: WorkspaceContext;
let folderContext: FolderContext;
const uri = testAssetUri("defaultPackage/Sources/PackageExe/main.swift");
const expectedDefinitionUri = testAssetUri(
"defaultPackage/Sources/PackageLib/PackageLib.swift"
);
// Position of the symbol 'a' in main.swift
const position = new vscode.Position(2, 6);

activateExtensionForSuite({
async setup(ctx) {
workspaceContext = ctx;
// Wait for a clean starting point, and run the build task for the fixture
await waitForNoRunningTasks();
folderContext = await folderInRootWorkspace("defaultPackage", workspaceContext);
await workspaceContext.focusFolder(folderContext);

const tasks = await vscode.tasks.fetchTasks({ type: "swift" });
const buildTask = tasks.find(t => t.name === "Build All (defaultPackage)");
const { exitCode, output } = await executeTaskAndWaitForResult(buildTask as SwiftTask);
expect(exitCode, `${output}`).to.equal(0);

// Ensure lsp client is ready
clientManager = workspaceContext.languageClientManager;
const clientState = await waitForClientState(clientManager, langclient.State.Running);
expect(clientState).to.equals(langclient.State.Running);
},
});

test("Goto Definition", async function () {
// Focus on the file of interest
const editor = await vscode.window.showTextDocument(uri);
const document = editor.document;

// Position of the symbol 'a' in main.swift
const definitionLocations = await vscode.commands.executeCommand<vscode.Location[]>(
"vscode.executeDefinitionProvider",
document.uri,
position
);

expect(definitionLocations).to.have.lengthOf(1, "There should be one definition of 'a'.");

const definition = definitionLocations[0];

// Assert that the definition is in PackageLib.swift at line 0
expect(definition.uri.toString()).to.equal(expectedDefinitionUri.toString());
expect(definition.range.start.line).to.equal(0);
});

test("Find All References", async function () {
// Focus on the file of interest
const editor = await vscode.window.showTextDocument(uri);
const document = editor.document;

const referenceLocations = await vscode.commands.executeCommand<vscode.Location[]>(
"vscode.executeReferenceProvider",
document.uri,
position
);

// We expect 2 references - one in `main.swift` and one in `PackageLib.swift`
expect(referenceLocations).to.have.lengthOf(2, "There should be two references to 'a'.");

// Extract reference URIs and sort them to have a predictable order
const referenceUris = referenceLocations.map(ref => ref.uri.toString()).sort();
const expectedUris = [
uri.toString(), // Reference in main.swift
expectedDefinitionUri.toString(), // Reference in PackageLib.swift
].sort();

expect(referenceUris).to.deep.equal(expectedUris);
});
});
21 changes: 10 additions & 11 deletions test/integration-tests/tasks/SwiftTaskProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
createSwiftTask,
createBuildAllTask,
getBuildAllTask,
SwiftTask,
} from "../../../src/tasks/SwiftTaskProvider";
import { SwiftToolchain } from "../../../src/toolchain/toolchain";
import {
Expand Down Expand Up @@ -63,9 +64,9 @@ suite("SwiftTaskProvider Test Suite", () => {
{ cwd: workspaceFolder.uri, scope: vscode.TaskScope.Workspace },
toolchain
);
const { exitCode } = await executeTaskAndWaitForResult(task);
expect(exitCode).to.equal(0);
});
const { exitCode, output } = await executeTaskAndWaitForResult(task);
expect(exitCode, `${output}`).to.equal(0);
}).timeout(10000);

test("Exit code on failure", async () => {
const task = createSwiftTask(
Expand Down Expand Up @@ -95,9 +96,9 @@ suite("SwiftTaskProvider Test Suite", () => {
new Version(1, 2, 3)
)
);
const { exitCode } = await executeTaskAndWaitForResult(task);
expect(exitCode).to.not.equal(0);
});
const { exitCode, output } = await executeTaskAndWaitForResult(task);
expect(exitCode, `${output}`).to.not.equal(0);
}).timeout(10000);
});

suite("provideTasks", () => {
Expand All @@ -115,12 +116,10 @@ suite("SwiftTaskProvider Test Suite", () => {
.and.to.include("-Xswiftc -diagnostic-style=llvm");
});

test("executes @slow", async () => {
test("executes", async () => {
assert(task);
const exitPromise = waitForEndTaskProcess(task);
await vscode.tasks.executeTask(task);
const exitCode = await exitPromise;
expect(exitCode).to.equal(0);
const { exitCode, output } = await executeTaskAndWaitForResult(task as SwiftTask);
expect(exitCode, `${output}`).to.equal(0);
}).timeout(180000); // 3 minutes to build
});

Expand Down

0 comments on commit 34c4d9c

Please sign in to comment.