Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add integration tests for build commands #1185

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
12 changes: 9 additions & 3 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ export function registerToolchainCommands(
];
}

export enum Commands {
RUN = "swift.run",
DEBUG = "swift.debug",
CLEAN_BUILD = "swift.cleanBuild",
}

/**
* Registers this extension's commands in the given {@link vscode.ExtensionContext context}.
*/
Expand All @@ -74,9 +80,9 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
resolveDependencies(ctx)
),
vscode.commands.registerCommand("swift.updateDependencies", () => updateDependencies(ctx)),
vscode.commands.registerCommand("swift.run", () => runBuild(ctx)),
vscode.commands.registerCommand("swift.debug", () => debugBuild(ctx)),
vscode.commands.registerCommand("swift.cleanBuild", () => cleanBuild(ctx)),
vscode.commands.registerCommand(Commands.RUN, () => runBuild(ctx)),
vscode.commands.registerCommand(Commands.DEBUG, () => debugBuild(ctx)),
vscode.commands.registerCommand(Commands.CLEAN_BUILD, () => cleanBuild(ctx)),
vscode.commands.registerCommand("swift.runTestsMultipleTimes", item => {
if (ctx.currentFolder) {
return runTestMultipleTimes(ctx.currentFolder, item, false);
Expand Down
8 changes: 4 additions & 4 deletions src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import { FolderContext } from "../FolderContext";
* Executes a {@link vscode.Task task} to run swift target.
*/
export async function runBuild(ctx: WorkspaceContext) {
await debugBuildWithOptions(ctx, { noDebug: true });
return await debugBuildWithOptions(ctx, { noDebug: true });
}

/**
* Executes a {@link vscode.Task task} to debug swift target.
*/
export async function debugBuild(ctx: WorkspaceContext) {
await debugBuildWithOptions(ctx, {});
return await debugBuildWithOptions(ctx, {});
}

/**
Expand All @@ -41,7 +41,7 @@ export async function cleanBuild(ctx: WorkspaceContext) {
if (!current) {
return;
}
await folderCleanBuild(current);
return await folderCleanBuild(current);
}

/**
Expand All @@ -62,7 +62,7 @@ export async function folderCleanBuild(folderContext: FolderContext) {
folderContext.workspaceContext.toolchain
);

await executeTaskWithUI(task, "Clean Build", folderContext);
return await executeTaskWithUI(task, "Clean Build", folderContext);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/debugger/launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,17 @@ export async function debugLaunchConfig(
config: vscode.DebugConfiguration,
options: vscode.DebugSessionOptions = {}
) {
return new Promise<void>((resolve, reject) => {
return new Promise<boolean>((resolve, reject) => {
vscode.debug.startDebugging(workspaceFolder, config, options).then(
started => {
if (started) {
const terminateSession = vscode.debug.onDidTerminateDebugSession(async () => {
// dispose terminate debug handler
terminateSession.dispose();
resolve();
resolve(true);
});
} else {
resolve(false);
}
},
reason => {
Expand Down
3 changes: 2 additions & 1 deletion src/ui/ReloadExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//

import * as vscode from "vscode";
import { Workbench } from "../utilities/commands";

/**
* Prompts the user to reload the extension in cases where we are unable to do
Expand All @@ -29,7 +30,7 @@ export async function showReloadExtensionNotification<T extends string>(
const buttons: ("Reload Extensions" | T)[] = ["Reload Extensions", ...items];
const selected = await vscode.window.showWarningMessage(message, ...buttons);
if (selected === "Reload Extensions") {
await vscode.commands.executeCommand("workbench.action.reloadWindow");
await vscode.commands.executeCommand(Workbench.ACTION_RELOADWINDOW);
}
return selected;
}
19 changes: 19 additions & 0 deletions src/utilities/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

export enum Workbench {
ACTION_DEBUG_CONTINUE = "workbench.action.debug.continue",
ACTION_CLOSEALLEDITORS = "workbench.action.closeAllEditors",
ACTION_RELOADWINDOW = "workbench.action.reloadWindow",
}
3 changes: 2 additions & 1 deletion test/integration-tests/BackgroundCompilation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { WorkspaceContext } from "../../src/WorkspaceContext";
import { globalWorkspaceContextPromise } from "./extension.test";
import { testAssetUri } from "../fixtures";
import { waitForNoRunningTasks } from "../utilities";
import { Workbench } from "../../src/utilities/commands";

suite("BackgroundCompilation Test Suite", () => {
let workspaceContext: WorkspaceContext;
Expand All @@ -31,7 +32,7 @@ suite("BackgroundCompilation Test Suite", () => {

suiteTeardown(async () => {
await vscode.workspace.getConfiguration("swift").update("backgroundCompilation", undefined);
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
});

test("build all on save @slow", async () => {
Expand Down
3 changes: 2 additions & 1 deletion test/integration-tests/DiagnosticsManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { DiagnosticsManager } from "../../src/DiagnosticsManager";
import { FolderContext } from "../../src/FolderContext";
import { Version } from "../../src/utilities/version";
import { folderContextPromise, globalWorkspaceContextPromise } from "./extension.test";
import { Workbench } from "../../src/utilities/commands";

const waitForDiagnostics = (uris: vscode.Uri[], allowEmpty: boolean = true) =>
new Promise<void>(res =>
Expand Down Expand Up @@ -907,7 +908,7 @@ suite("DiagnosticsManager Test Suite", async function () {
});

teardown(async () => {
await vscode.commands.executeCommand("workbench.action.closeAllEditors");
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
});

test("Provides swift diagnostics", async () => {
Expand Down
91 changes: 91 additions & 0 deletions test/integration-tests/commands/build.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//===----------------------------------------------------------------------===//
//
// 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 fs from "fs";
import * as path from "path";
import { expect } from "chai";
import { folderContextPromise, globalWorkspaceContextPromise } from "../extension.test";
import { waitForNoRunningTasks } from "../../utilities";
import { testAssetUri } from "../../fixtures";
import { FolderContext } from "../../../src/FolderContext";
import { WorkspaceContext } from "../../../src/WorkspaceContext";
import { Commands } from "../../../src/commands";
import { makeDebugConfigurations } from "../../../src/debugger/launch";
import { Workbench } from "../../../src/utilities/commands";
import { continueSession, waitForDebugAdapterCommand } from "../../utilities/debug";

suite("Build Commands", function () {
let folderContext: FolderContext;
let workspaceContext: WorkspaceContext;
const uri = testAssetUri("defaultPackage/Sources/PackageExe/main.swift");
const breakpoints = [
new vscode.SourceBreakpoint(new vscode.Location(uri, new vscode.Position(2, 0))),
];

suiteSetup(async function () {
workspaceContext = await globalWorkspaceContextPromise;
await waitForNoRunningTasks();
folderContext = await folderContextPromise("defaultPackage");
await workspaceContext.focusFolder(folderContext);
await vscode.window.showTextDocument(uri);
michael-weng marked this conversation as resolved.
Show resolved Hide resolved
makeDebugConfigurations(folderContext, undefined, true);
});

suiteTeardown(async () => {
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
});

test("Swift: Run Build", async () => {
// A breakpoint will have not effect on the Run command.
vscode.debug.addBreakpoints(breakpoints);

const result = await vscode.commands.executeCommand(Commands.RUN);
expect(result).to.be.true;

vscode.debug.removeBreakpoints(breakpoints);
});

test("Swift: Clean Build", async () => {
const buildPath = path.join(folderContext.folder.fsPath, ".build");
const beforeItemCount = fs.readdirSync(buildPath).length;

const result = await vscode.commands.executeCommand(Commands.CLEAN_BUILD);
expect(result).to.be.true;

const afterItemCount = fs.readdirSync(buildPath).length;
// This test will run in order after the Swift: Run Build test,
// where .build folder is going to be filled with built artifacts.
// After executing the clean command the build directory is guranteed to have less entry.
expect(afterItemCount).to.be.lessThan(beforeItemCount);
michael-weng marked this conversation as resolved.
Show resolved Hide resolved
});

test("Swift: Debug Build @slow", async () => {
vscode.debug.addBreakpoints(breakpoints);
// Promise used to indicate we hit the break point.
// NB: "stopped" is the exact command when debuggee has stopped due to break point,
// but "stackTrace" is the deterministic sync point we will use to make sure we can execute continue
const bpPromise = waitForDebugAdapterCommand(
"Debug PackageExe (defaultPackage)",
"stackTrace",
workspaceContext
);

const result = vscode.commands.executeCommand(Commands.DEBUG);
expect(result).to.eventually.be.true;

await bpPromise.then(() => continueSession());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're hitting breakpoints, crossing into smoke level, let's create a smoke suite and put that there

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that's what @slow do right now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still should split into separate suite. Is it actually slow or just trying to keep out of CI build?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are we trying to achieve to split this into a separate suite? Is it for dependency management?

Given the test run fast and this is a command that should probably always work, I am not sure if we want keep this out of CI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will leave this for until long term approach is decided

vscode.debug.removeBreakpoints(breakpoints);
});
});
3 changes: 2 additions & 1 deletion test/integration-tests/editor/CommentCompletion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import * as assert from "assert";
import * as vscode from "vscode";
import { CommentCompletionProviders } from "../../../src/editor/CommentCompletion";
import { Workbench } from "../../../src/utilities/commands";

suite("CommentCompletion Test Suite", () => {
let document: vscode.TextDocument | undefined;
Expand All @@ -31,7 +32,7 @@ suite("CommentCompletion Test Suite", () => {

if (editor && document) {
await vscode.window.showTextDocument(document, editor.viewColumn);
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
}

provider.dispose();
Expand Down
3 changes: 2 additions & 1 deletion test/unit-tests/ui/ReloadExtension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { expect } from "chai";
import { mockGlobalObject } from "../../MockUtils";
import * as vscode from "vscode";
import { showReloadExtensionNotification } from "../../../src/ui/ReloadExtension";
import { Workbench } from "../../../src/utilities/commands";

suite("showReloadExtensionNotification()", async function () {
const mockedVSCodeWindow = mockGlobalObject(vscode, "window");
Expand All @@ -38,7 +39,7 @@ suite("showReloadExtensionNotification()", async function () {
await showReloadExtensionNotification("Want to reload?");

expect(mockedVSCodeCommands.executeCommand).to.have.been.calledOnceWithExactly(
"workbench.action.reloadWindow"
Workbench.ACTION_RELOADWINDOW
);
});

Expand Down
78 changes: 78 additions & 0 deletions test/utilities/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===----------------------------------------------------------------------===//
//
// 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 { Workbench } from "../../src/utilities/commands";
import { DebugAdapter } from "../../src/debugger/debugAdapter";
import { WorkspaceContext } from "../../src/WorkspaceContext";

export async function continueSession(): Promise<void> {
await vscode.commands.executeCommand(Workbench.ACTION_DEBUG_CONTINUE);
}

/**
* Waits for a specific message from the debug adapter.
*
* @param name The name of the debug session to wait for.
* @param matches A function to match the desired message.
* @param workspaceContext The workspace context containing toolchain information.
* @returns A promise that resolves with the matching message.
*/
export async function waitForDebugAdapterMessage(
name: string,
matches: (message: any) => boolean,
workspaceContext: WorkspaceContext
): Promise<any> {
return await new Promise<any>(res => {
const disposable = vscode.debug.registerDebugAdapterTrackerFactory(
DebugAdapter.getLaunchConfigType(workspaceContext.toolchain.swiftVersion),
{
createDebugAdapterTracker: function (
session: vscode.DebugSession
): vscode.ProviderResult<vscode.DebugAdapterTracker> {
if (session.name !== name) {
return;
}
return {
onDidSendMessage(message) {
if (matches(message)) {
disposable.dispose();
res(message);
}
},
};
},
}
);
});
}

/**
* Waits for a specific command to be sent by the debug adapter.
*
* @param name The name of the debug session to wait for.
* @param command The command to wait for.
* @param workspaceContext The workspace context containing toolchain information.
* @returns A promise that resolves with the matching command message.
*/
export async function waitForDebugAdapterCommand(
name: string,
command: string,
workspaceContext: WorkspaceContext
): Promise<any> {
return await waitForDebugAdapterMessage(
name,
(m: any) => m.command === command,
workspaceContext
);
}
Loading