Skip to content

Commit

Permalink
Fix macro expansion test (swiftlang#1301)
Browse files Browse the repository at this point in the history
* Fix macro expansion test

Wait for document to be indexed before proceeding

Issue: swiftlang#1286

* Catch unexpected errors

* Use _pollIndex request

* Some cleanup
  • Loading branch information
award999 authored Jan 8, 2025
1 parent 182a0d7 commit 3398bd2
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 22 deletions.
2 changes: 1 addition & 1 deletion assets/test/swift-macro/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.0-latest"),
.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.1"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
38 changes: 17 additions & 21 deletions test/integration-tests/language/LanguageClientIntegration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,51 +22,45 @@ import { executeTaskAndWaitForResult, waitForNoRunningTasks } from "../../utilit
import { getBuildAllTask, SwiftTask } from "../../../src/tasks/SwiftTaskProvider";
import { Version } from "../../../src/utilities/version";
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;
}
import { FolderContext } from "../../../src/FolderContext";
import { waitForClientState, waitForCodeActions, waitForIndex } from "../utilities/lsputilities";

async function buildProject(ctx: WorkspaceContext, name: string) {
await waitForNoRunningTasks();
const folderContext = await folderInRootWorkspace(name, ctx);
const task = (await getBuildAllTask(folderContext)) as SwiftTask;
const { exitCode, output } = await executeTaskAndWaitForResult(task);
expect(exitCode, `${output}`).to.equal(0);
return folderContext;
}

suite("Language Client Integration Suite @slow", function () {
this.timeout(5 * 60 * 1000);

let clientManager: LanguageClientManager;
let workspaceContext: WorkspaceContext;
let macroFolderContext: FolderContext;

activateExtensionForSuite({
async setup(ctx) {
this.timeout(5 * 60 * 1000);

workspaceContext = ctx;

// Wait for a clean starting point, and build all tasks for the fixture
if (workspaceContext.swiftVersion.isGreaterThanOrEqual(new Version(6, 1, 0))) {
await buildProject(ctx, "swift-macro");
macroFolderContext = await buildProject(ctx, "swift-macro");
}
await buildProject(ctx, "defaultPackage");

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

setup(async () => {
await waitForIndex(workspaceContext.languageClientManager);
});

test("Expand Macro", async function () {
// Expand Macro support in Swift started from 6.1
if (workspaceContext.swiftVersion.isLessThan(new Version(6, 1, 0))) {
Expand All @@ -76,12 +70,15 @@ suite("Language Client Integration Suite @slow", function () {
// Focus on the file of interest
const uri = testAssetUri("swift-macro/Sources/swift-macroClient/main.swift");
await vscode.window.showTextDocument(uri);
await workspaceContext.focusFolder(macroFolderContext);

// Beginning of macro, #
const position = new vscode.Position(5, 21);

// Create a range starting and ending at the specified position
const range = new vscode.Range(position, position);
const range = new vscode.Selection(position, position.with({ character: 22 }));

await waitForCodeActions(workspaceContext.languageClientManager, uri, range);

// Execute the code action provider command
const codeActions = await vscode.commands.executeCommand<vscode.CodeAction[]>(
Expand All @@ -90,8 +87,6 @@ suite("Language Client Integration Suite @slow", function () {
range
);

const expectedMacro = '(a + b, "a + b")';

// Find the "expand.macro.command" action
const expandMacroAction = codeActions.find(
action => action.command?.command === "expand.macro.command"
Expand Down Expand Up @@ -129,6 +124,7 @@ suite("Language Client Integration Suite @slow", function () {
expect(referenceDocument).to.not.be.undefined;

// Assert that the content contains the expected result
const expectedMacro = '(a + b, "a + b")';
const content = referenceDocument.getText();
expect(content).to.include(expectedMacro);
});
Expand Down
87 changes: 87 additions & 0 deletions test/integration-tests/utilities/lsputilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the VS Code Swift open source project
//
// Copyright (c) 2025 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 { LanguageClientManager } from "../../../src/sourcekit-lsp/LanguageClientManager";

export async function waitForClient<Result>(
languageClientManager: LanguageClientManager,
getResult: (
c: langclient.LanguageClient,
token: langclient.CancellationToken
) => Promise<Result>,
match: (r: Result | undefined) => boolean
): Promise<Result | undefined> {
let result: Result | undefined = undefined;
while (!match(result)) {
result = await languageClientManager.useLanguageClient<Result>(getResult);
console.warn("Language client is not ready yet. Retrying in 100 ms...");
await new Promise(resolve => setTimeout(resolve, 100));
}
return result;
}

// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace PollIndexRequest {
export const method = "workspace/_pollIndex" as const;
export const messageDirection: langclient.MessageDirection =
langclient.MessageDirection.clientToServer;
export const type = new langclient.RequestType<object, object, never>(method);
}

export async function waitForIndex(languageClientManager: LanguageClientManager): Promise<void> {
await languageClientManager.useLanguageClient(async (client, token) =>
client.sendRequest(PollIndexRequest.type, {}, token)
);
}

export async function waitForClientState(
languageClientManager: LanguageClientManager,
expectedState: langclient.State
): Promise<langclient.State | undefined> {
return await waitForClient(
languageClientManager,
async c => c.state,
s => s === expectedState
);
}

export async function waitForCodeActions(
languageClientManager: LanguageClientManager,
uri: vscode.Uri,
range: vscode.Range
): Promise<(langclient.CodeAction | langclient.Command)[]> {
return (
(await waitForClient(
languageClientManager,
async (client, token) => {
try {
return client.sendRequest(
langclient.CodeActionRequest.type,
{
context: langclient.CodeActionContext.create([]),
textDocument: langclient.TextDocumentIdentifier.create(uri.toString()),
range,
},
token
);
} catch (e) {
// Ignore
}
},
s => (s || []).length > 0
)) || []
);
}

0 comments on commit 3398bd2

Please sign in to comment.