Skip to content

Commit d6590e8

Browse files
authored
Add integration tests for build commands (#1185)
* Add integration tests for build commands - Validate the workflow of user calling the Swift: Run Build/Clean Build/Debug Build commands. - Ensure Swift: Run Build will not get blocked by pre-set breakpoint. - Ensure Swift: Clean Build will result in a cleaned up .build folder. - Ensure Swift: Debug Build will stop on a breakpoint and resume. Issue: #1184 * - Added module enum for workbench commands string constant - Added comments for clarification - Added utilities to listen for dap message, this is useful for test synchronization. Code takes inspiration from #1126 * - Rename from utilies/command.ts to utilies/commands.ts - Minor cosmetic change to utilies/commands.ts * - Fix a bug in updateSettings where promise is not being exlicitly returned, causing restore of setting being not awaitable - Make makeDebugConfigurations to be awaitable - Change launch to also update the key for ASLR disable settings - Make the test properly set up and reset the settings that update the launch config
1 parent 38369e8 commit d6590e8

File tree

13 files changed

+234
-21
lines changed

13 files changed

+234
-21
lines changed

src/commands.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ export function registerToolchainCommands(
6464
];
6565
}
6666

67+
export enum Commands {
68+
RUN = "swift.run",
69+
DEBUG = "swift.debug",
70+
CLEAN_BUILD = "swift.cleanBuild",
71+
}
72+
6773
/**
6874
* Registers this extension's commands in the given {@link vscode.ExtensionContext context}.
6975
*/
@@ -74,9 +80,9 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
7480
resolveDependencies(ctx)
7581
),
7682
vscode.commands.registerCommand("swift.updateDependencies", () => updateDependencies(ctx)),
77-
vscode.commands.registerCommand("swift.run", () => runBuild(ctx)),
78-
vscode.commands.registerCommand("swift.debug", () => debugBuild(ctx)),
79-
vscode.commands.registerCommand("swift.cleanBuild", () => cleanBuild(ctx)),
83+
vscode.commands.registerCommand(Commands.RUN, () => runBuild(ctx)),
84+
vscode.commands.registerCommand(Commands.DEBUG, () => debugBuild(ctx)),
85+
vscode.commands.registerCommand(Commands.CLEAN_BUILD, () => cleanBuild(ctx)),
8086
vscode.commands.registerCommand("swift.runTestsMultipleTimes", item => {
8187
if (ctx.currentFolder) {
8288
return runTestMultipleTimes(ctx.currentFolder, item, false);

src/commands/build.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ import { FolderContext } from "../FolderContext";
2323
* Executes a {@link vscode.Task task} to run swift target.
2424
*/
2525
export async function runBuild(ctx: WorkspaceContext) {
26-
await debugBuildWithOptions(ctx, { noDebug: true });
26+
return await debugBuildWithOptions(ctx, { noDebug: true });
2727
}
2828

2929
/**
3030
* Executes a {@link vscode.Task task} to debug swift target.
3131
*/
3232
export async function debugBuild(ctx: WorkspaceContext) {
33-
await debugBuildWithOptions(ctx, {});
33+
return await debugBuildWithOptions(ctx, {});
3434
}
3535

3636
/**
@@ -41,7 +41,7 @@ export async function cleanBuild(ctx: WorkspaceContext) {
4141
if (!current) {
4242
return;
4343
}
44-
await folderCleanBuild(current);
44+
return await folderCleanBuild(current);
4545
}
4646

4747
/**
@@ -62,7 +62,7 @@ export async function folderCleanBuild(folderContext: FolderContext) {
6262
folderContext.workspaceContext.toolchain
6363
);
6464

65-
await executeTaskWithUI(task, "Clean Build", folderContext);
65+
return await executeTaskWithUI(task, "Clean Build", folderContext);
6666
}
6767

6868
/**

src/debugger/launch.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ import { CI_DISABLE_ASLR } from "./lldb";
2929
* @param ctx folder context to create launch configurations for
3030
* @param yes automatically answer yes to dialogs
3131
*/
32-
export async function makeDebugConfigurations(ctx: FolderContext, message?: string, yes = false) {
32+
export async function makeDebugConfigurations(
33+
ctx: FolderContext,
34+
message?: string,
35+
yes = false
36+
): Promise<boolean> {
3337
if (!configuration.folder(ctx.workspaceFolder).autoGenerateLaunchConfigurations) {
34-
return;
38+
return false;
3539
}
3640
const wsLaunchSection = vscode.workspace.getConfiguration("launch", ctx.folder);
3741
const launchConfigs = wsLaunchSection.get<vscode.DebugConfiguration[]>("configurations") || [];
@@ -41,6 +45,8 @@ export async function makeDebugConfigurations(ctx: FolderContext, message?: stri
4145
"cwd",
4246
"preLaunchTask",
4347
"type",
48+
"disableASLR",
49+
"initCommands",
4450
`env.${swiftLibraryPathKey()}`,
4551
];
4652
const configUpdates: { index: number; config: vscode.DebugConfiguration }[] = [];
@@ -96,6 +102,7 @@ export async function makeDebugConfigurations(ctx: FolderContext, message?: stri
96102
vscode.ConfigurationTarget.WorkspaceFolder
97103
);
98104
}
105+
return true;
99106
}
100107

101108
// Return debug launch configuration for an executable in the given folder
@@ -178,15 +185,17 @@ export async function debugLaunchConfig(
178185
config: vscode.DebugConfiguration,
179186
options: vscode.DebugSessionOptions = {}
180187
) {
181-
return new Promise<void>((resolve, reject) => {
188+
return new Promise<boolean>((resolve, reject) => {
182189
vscode.debug.startDebugging(workspaceFolder, config, options).then(
183190
started => {
184191
if (started) {
185192
const terminateSession = vscode.debug.onDidTerminateDebugSession(async () => {
186193
// dispose terminate debug handler
187194
terminateSession.dispose();
188-
resolve();
195+
resolve(true);
189196
});
197+
} else {
198+
resolve(false);
190199
}
191200
},
192201
reason => {

src/ui/ReloadExtension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import * as vscode from "vscode";
16+
import { Workbench } from "../utilities/commands";
1617

1718
/**
1819
* Prompts the user to reload the extension in cases where we are unable to do
@@ -29,7 +30,7 @@ export async function showReloadExtensionNotification<T extends string>(
2930
const buttons: ("Reload Extensions" | T)[] = ["Reload Extensions", ...items];
3031
const selected = await vscode.window.showWarningMessage(message, ...buttons);
3132
if (selected === "Reload Extensions") {
32-
await vscode.commands.executeCommand("workbench.action.reloadWindow");
33+
await vscode.commands.executeCommand(Workbench.ACTION_RELOADWINDOW);
3334
}
3435
return selected;
3536
}

src/utilities/commands.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2024 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
export enum Workbench {
16+
ACTION_DEBUG_CONTINUE = "workbench.action.debug.continue",
17+
ACTION_CLOSEALLEDITORS = "workbench.action.closeAllEditors",
18+
ACTION_RELOADWINDOW = "workbench.action.reloadWindow",
19+
}

test/integration-tests/BackgroundCompilation.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { WorkspaceContext } from "../../src/WorkspaceContext";
1818
import { globalWorkspaceContextPromise } from "./extension.test";
1919
import { testAssetUri } from "../fixtures";
2020
import { waitForNoRunningTasks } from "../utilities";
21+
import { Workbench } from "../../src/utilities/commands";
2122

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

3233
suiteTeardown(async () => {
3334
await vscode.workspace.getConfiguration("swift").update("backgroundCompilation", undefined);
34-
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
35+
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
3536
});
3637

3738
test("build all on save @slow", async () => {

test/integration-tests/DiagnosticsManager.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { DiagnosticsManager } from "../../src/DiagnosticsManager";
2323
import { FolderContext } from "../../src/FolderContext";
2424
import { Version } from "../../src/utilities/version";
2525
import { folderContextPromise, globalWorkspaceContextPromise } from "./extension.test";
26+
import { Workbench } from "../../src/utilities/commands";
2627

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

909910
teardown(async () => {
910-
await vscode.commands.executeCommand("workbench.action.closeAllEditors");
911+
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
911912
});
912913

913914
test("Provides swift diagnostics", async () => {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2024 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as vscode from "vscode";
16+
import * as fs from "fs";
17+
import * as path from "path";
18+
import { expect } from "chai";
19+
import { folderContextPromise, globalWorkspaceContextPromise } from "../extension.test";
20+
import { waitForNoRunningTasks } from "../../utilities";
21+
import { testAssetUri } from "../../fixtures";
22+
import { FolderContext } from "../../../src/FolderContext";
23+
import { WorkspaceContext } from "../../../src/WorkspaceContext";
24+
import { Commands } from "../../../src/commands";
25+
import { makeDebugConfigurations } from "../../../src/debugger/launch";
26+
import { Workbench } from "../../../src/utilities/commands";
27+
import { continueSession, waitForDebugAdapterCommand } from "../../utilities/debug";
28+
import { SettingsMap, updateSettings } from "../testexplorer/utilities";
29+
30+
suite("Build Commands", function () {
31+
let folderContext: FolderContext;
32+
let workspaceContext: WorkspaceContext;
33+
let settingsTeardown: () => Promise<SettingsMap>;
34+
const uri = testAssetUri("defaultPackage/Sources/PackageExe/main.swift");
35+
const breakpoints = [
36+
new vscode.SourceBreakpoint(new vscode.Location(uri, new vscode.Position(2, 0))),
37+
];
38+
39+
suiteSetup(async function () {
40+
workspaceContext = await globalWorkspaceContextPromise;
41+
await waitForNoRunningTasks();
42+
folderContext = await folderContextPromise("defaultPackage");
43+
await workspaceContext.focusFolder(folderContext);
44+
await vscode.window.showTextDocument(uri);
45+
settingsTeardown = await updateSettings({
46+
"swift.autoGenerateLaunchConfigurations": true,
47+
});
48+
await makeDebugConfigurations(folderContext, undefined, true);
49+
});
50+
51+
suiteTeardown(async () => {
52+
await settingsTeardown();
53+
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
54+
});
55+
56+
test("Swift: Run Build", async () => {
57+
// A breakpoint will have not effect on the Run command.
58+
vscode.debug.addBreakpoints(breakpoints);
59+
60+
const result = await vscode.commands.executeCommand(Commands.RUN);
61+
expect(result).to.be.true;
62+
63+
vscode.debug.removeBreakpoints(breakpoints);
64+
});
65+
66+
test("Swift: Clean Build", async () => {
67+
const buildPath = path.join(folderContext.folder.fsPath, ".build");
68+
const beforeItemCount = fs.readdirSync(buildPath).length;
69+
70+
const result = await vscode.commands.executeCommand(Commands.CLEAN_BUILD);
71+
expect(result).to.be.true;
72+
73+
const afterItemCount = fs.readdirSync(buildPath).length;
74+
// This test will run in order after the Swift: Run Build test,
75+
// where .build folder is going to be filled with built artifacts.
76+
// After executing the clean command the build directory is guranteed to have less entry.
77+
expect(afterItemCount).to.be.lessThan(beforeItemCount);
78+
});
79+
80+
test("Swift: Debug Build @slow", async () => {
81+
vscode.debug.addBreakpoints(breakpoints);
82+
// Promise used to indicate we hit the break point.
83+
// NB: "stopped" is the exact command when debuggee has stopped due to break point,
84+
// but "stackTrace" is the deterministic sync point we will use to make sure we can execute continue
85+
const bpPromise = waitForDebugAdapterCommand(
86+
"Debug PackageExe (defaultPackage)",
87+
"stackTrace",
88+
workspaceContext
89+
);
90+
91+
const result = vscode.commands.executeCommand(Commands.DEBUG);
92+
expect(result).to.eventually.be.true;
93+
94+
await bpPromise.then(() => continueSession());
95+
vscode.debug.removeBreakpoints(breakpoints);
96+
});
97+
});

test/integration-tests/editor/CommentCompletion.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import * as assert from "assert";
1616
import * as vscode from "vscode";
1717
import { CommentCompletionProviders } from "../../../src/editor/CommentCompletion";
18+
import { Workbench } from "../../../src/utilities/commands";
1819

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

3233
if (editor && document) {
3334
await vscode.window.showTextDocument(document, editor.viewColumn);
34-
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
35+
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
3536
}
3637

3738
provider.dispose();

test/integration-tests/testexplorer/TestExplorerIntegration.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
eventPromise,
2525
gatherTests,
2626
runTest,
27+
SettingsMap,
2728
setupTestExplorerTest,
2829
waitForTestExplorerReady,
2930
} from "./utilities";
@@ -51,7 +52,7 @@ suite("Test Explorer Suite", function () {
5152
let testExplorer: TestExplorer;
5253

5354
suite("Debugging", function () {
54-
let settingsTeardown: () => void;
55+
let settingsTeardown: () => Promise<SettingsMap>;
5556

5657
async function runXCTest() {
5758
const suiteId = "PackageTests.PassingXCTestSuite";

0 commit comments

Comments
 (0)