From 91f7852c52ab6a02cad9954ee1481610c67ba9f2 Mon Sep 17 00:00:00 2001 From: "Michael (SPG) Weng" Date: Thu, 31 Oct 2024 14:39:28 -0400 Subject: [PATCH] - 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 https://github.com/swiftlang/vscode-swift/pull/1126 --- src/ui/ReloadExtension.ts | 3 +- src/utilities/command.ts | 19 +++++ .../BackgroundCompilation.test.ts | 3 +- .../DiagnosticsManager.test.ts | 3 +- test/integration-tests/commands/build.test.ts | 19 ++++- .../editor/CommentCompletion.test.ts | 3 +- test/unit-tests/ui/ReloadExtension.test.ts | 3 +- test/utilities/debug.ts | 78 +++++++++++++++++++ 8 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 src/utilities/command.ts create mode 100644 test/utilities/debug.ts diff --git a/src/ui/ReloadExtension.ts b/src/ui/ReloadExtension.ts index 2ae9f2c8d..47b623cb9 100644 --- a/src/ui/ReloadExtension.ts +++ b/src/ui/ReloadExtension.ts @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import * as vscode from "vscode"; +import { Workbench } from "../utilities/command"; /** * Prompts the user to reload the extension in cases where we are unable to do @@ -29,7 +30,7 @@ export async function showReloadExtensionNotification( 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; } diff --git a/src/utilities/command.ts b/src/utilities/command.ts new file mode 100644 index 000000000..54d0c724a --- /dev/null +++ b/src/utilities/command.ts @@ -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", +} diff --git a/test/integration-tests/BackgroundCompilation.test.ts b/test/integration-tests/BackgroundCompilation.test.ts index 08b14961a..7332697dc 100644 --- a/test/integration-tests/BackgroundCompilation.test.ts +++ b/test/integration-tests/BackgroundCompilation.test.ts @@ -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/command"; suite("BackgroundCompilation Test Suite", () => { let workspaceContext: WorkspaceContext; @@ -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 () => { diff --git a/test/integration-tests/DiagnosticsManager.test.ts b/test/integration-tests/DiagnosticsManager.test.ts index 28acdd340..4e22f94da 100644 --- a/test/integration-tests/DiagnosticsManager.test.ts +++ b/test/integration-tests/DiagnosticsManager.test.ts @@ -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/command"; const waitForDiagnostics = (uris: vscode.Uri[], allowEmpty: boolean = true) => new Promise(res => @@ -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 () => { diff --git a/test/integration-tests/commands/build.test.ts b/test/integration-tests/commands/build.test.ts index 9fc0ae98a..83b2c4c66 100644 --- a/test/integration-tests/commands/build.test.ts +++ b/test/integration-tests/commands/build.test.ts @@ -23,6 +23,8 @@ 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/command"; +import { continueSession, waitForDebugAdapterCommand } from "../../utilities/debug"; suite("Build Commands", function () { let folderContext: FolderContext; @@ -42,7 +44,7 @@ suite("Build Commands", function () { }); suiteTeardown(async () => { - await vscode.commands.executeCommand("workbench.action.closeAllEditors"); + await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS); }); test("Swift: Run Build", async () => { @@ -63,16 +65,27 @@ suite("Build Commands", function () { 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); }); - test("Swift: Debug Build", async () => { + 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 vscode.commands.executeCommand("workbench.action.debug.continue"); + await bpPromise.then(() => continueSession()); vscode.debug.removeBreakpoints(breakpoints); }); }); diff --git a/test/integration-tests/editor/CommentCompletion.test.ts b/test/integration-tests/editor/CommentCompletion.test.ts index 9f8655a91..13611f418 100644 --- a/test/integration-tests/editor/CommentCompletion.test.ts +++ b/test/integration-tests/editor/CommentCompletion.test.ts @@ -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/command"; suite("CommentCompletion Test Suite", () => { let document: vscode.TextDocument | undefined; @@ -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(); diff --git a/test/unit-tests/ui/ReloadExtension.test.ts b/test/unit-tests/ui/ReloadExtension.test.ts index 2c3b3052f..d7cb731f4 100644 --- a/test/unit-tests/ui/ReloadExtension.test.ts +++ b/test/unit-tests/ui/ReloadExtension.test.ts @@ -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/command"; suite("showReloadExtensionNotification()", async function () { const mockedVSCodeWindow = mockGlobalObject(vscode, "window"); @@ -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 ); }); diff --git a/test/utilities/debug.ts b/test/utilities/debug.ts new file mode 100644 index 000000000..3f929efc5 --- /dev/null +++ b/test/utilities/debug.ts @@ -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/command"; +import { DebugAdapter } from "../../src/debugger/debugAdapter"; +import { WorkspaceContext } from "../../src/WorkspaceContext"; + +export async function continueSession(): Promise { + 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 { + return await new Promise(res => { + const disposable = vscode.debug.registerDebugAdapterTrackerFactory( + DebugAdapter.getLaunchConfigType(workspaceContext.toolchain.swiftVersion), + { + createDebugAdapterTracker: function ( + session: vscode.DebugSession + ): vscode.ProviderResult { + 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 { + return await waitForDebugAdapterMessage( + name, + (m: any) => m.command === command, + workspaceContext + ); +}