From 5f58fb15e4d31ff435d9f7b9f515ad1590e7f7a9 Mon Sep 17 00:00:00 2001 From: Ahmed AbdelMagied Date: Sun, 9 Mar 2025 13:23:44 +0200 Subject: [PATCH 1/5] Define search for sub packages logic as utility for unit testing --- src/WorkspaceContext.ts | 48 +++++++--------------------- src/utilities/workspace.ts | 64 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 src/utilities/workspace.ts diff --git a/src/WorkspaceContext.ts b/src/WorkspaceContext.ts index 648b6776d..4095593d7 100644 --- a/src/WorkspaceContext.ts +++ b/src/WorkspaceContext.ts @@ -18,7 +18,7 @@ import { FolderContext } from "./FolderContext"; import { StatusItem } from "./ui/StatusItem"; import { SwiftOutputChannel } from "./ui/SwiftOutputChannel"; import { swiftLibraryPathKey } from "./utilities/utilities"; -import { pathExists, isPathInsidePath } from "./utilities/filesystem"; +import { isPathInsidePath } from "./utilities/filesystem"; import { LanguageClientManager } from "./sourcekit-lsp/LanguageClientManager"; import { TemporaryFolder } from "./utilities/tempFolder"; import { TaskManager } from "./tasks/TaskManager"; @@ -34,6 +34,7 @@ import { DiagnosticsManager } from "./DiagnosticsManager"; import { DocumentationManager } from "./documentation/DocumentationManager"; import { DocCDocumentationRequest, ReIndexProjectRequest } from "./sourcekit-lsp/extensions"; import { TestKind } from "./TestExplorer/TestKind"; +import { isValidWorkspaceFolder, searchForPackages } from "./utilities/workspace"; /** * Context for whole workspace. Holds array of contexts for each workspace folder @@ -391,38 +392,19 @@ export class WorkspaceContext implements vscode.Disposable { * @param folder folder being added */ async addWorkspaceFolder(workspaceFolder: vscode.WorkspaceFolder) { - await this.searchForPackages(workspaceFolder.uri, workspaceFolder); - - if (this.getActiveWorkspaceFolder(vscode.window.activeTextEditor) === workspaceFolder) { - await this.focusTextEditor(vscode.window.activeTextEditor); - } - } + const folders = await searchForPackages( + workspaceFolder.uri, + configuration.disableSwiftPMIntegration, + configuration.folder(workspaceFolder).searchSubfoldersForPackages + ); - async searchForPackages(folder: vscode.Uri, workspaceFolder: vscode.WorkspaceFolder) { - // add folder if Package.swift/compile_commands.json/compile_flags.txt/buildServer.json exists - if (await this.isValidWorkspaceFolder(folder.fsPath)) { + for (const folder of folders) { await this.addPackageFolder(folder, workspaceFolder); - return; - } - // should I search sub-folders for more Swift Packages - if (!configuration.folder(workspaceFolder).searchSubfoldersForPackages) { - return; } - await vscode.workspace.fs.readDirectory(folder).then(async entries => { - for (const entry of entries) { - if ( - entry[1] === vscode.FileType.Directory && - entry[0][0] !== "." && - entry[0] !== "Packages" - ) { - await this.searchForPackages( - vscode.Uri.joinPath(folder, entry[0]), - workspaceFolder - ); - } - } - }); + if (this.getActiveWorkspaceFolder(vscode.window.activeTextEditor) === workspaceFolder) { + await this.focusTextEditor(vscode.window.activeTextEditor); + } } public async addPackageFolder( @@ -597,13 +579,7 @@ export class WorkspaceContext implements vscode.Disposable { * Package.swift or a CMake compile_commands.json, compile_flags.txt, or a BSP buildServer.json. */ async isValidWorkspaceFolder(folder: string): Promise { - return ( - ((await pathExists(folder, "Package.swift")) && - !configuration.disableSwiftPMIntegration) || - (await pathExists(folder, "compile_commands.json")) || - (await pathExists(folder, "compile_flags.txt")) || - (await pathExists(folder, "buildServer.json")) - ); + return await isValidWorkspaceFolder(folder, configuration.disableSwiftPMIntegration); } /** send unfocus event to current focussed folder and clear current folder */ diff --git a/src/utilities/workspace.ts b/src/utilities/workspace.ts new file mode 100644 index 000000000..5fccec752 --- /dev/null +++ b/src/utilities/workspace.ts @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the VS Code Swift open source project +// +// Copyright (c) 2022 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 { pathExists } from "./filesystem"; + +export async function searchForPackages( + folder: vscode.Uri, + disableSwiftPMIntegration: boolean, + searchSubfoldersForPackages: boolean +): Promise> { + const folders: Array = []; + + async function search(folder: vscode.Uri) { + // add folder if Package.swift/compile_commands.json/compile_flags.txt/buildServer.json exists + if (await isValidWorkspaceFolder(folder.fsPath, disableSwiftPMIntegration)) { + folders.push(folder); + return; + } + // should I search sub-folders for more Swift Packages + if (!searchSubfoldersForPackages) { + return; + } + + await vscode.workspace.fs.readDirectory(folder).then(async entries => { + for (const entry of entries) { + if ( + entry[1] === vscode.FileType.Directory && + entry[0][0] !== "." && + entry[0] !== "Packages" + ) { + await search(vscode.Uri.joinPath(folder, entry[0])); + } + } + }); + } + + await search(folder); + + return folders; +} + +export async function isValidWorkspaceFolder( + folder: string, + disableSwiftPMIntegration: boolean +): Promise { + return ( + (!disableSwiftPMIntegration && (await pathExists(folder, "Package.swift"))) || + (await pathExists(folder, "compile_commands.json")) || + (await pathExists(folder, "compile_flags.txt")) || + (await pathExists(folder, "buildServer.json")) + ); +} From ab7241c2fe0d976cc93c051addf708715ad68a6c Mon Sep 17 00:00:00 2001 From: Ahmed AbdelMagied Date: Sun, 9 Mar 2025 13:24:38 +0200 Subject: [PATCH 2/5] Add test package with multiple subpackages --- .../Module1/.vscode/launch.json | 22 ++++++++++ .../test/ModularPackage/Module1/Package.swift | 23 +++++++++++ .../Module1/Sources/Module1/Module1.swift | 7 ++++ .../Sources/Module1Demo/Module1Demo.swift | 11 +++++ .../Module1/Tests/Module1Tests.swift | 20 ++++++++++ .../Module2/.vscode/launch.json | 40 +++++++++++++++++++ .../test/ModularPackage/Module2/Package.swift | 23 +++++++++++ .../Module2/Sources/Module2/Module2.swift | 7 ++++ .../Module2/Sources/Module2Demo/main.swift | 11 +++++ .../Module2/Tests/Module2Tests.swift | 20 ++++++++++ assets/test/ModularPackage/Package.swift | 11 +++++ 11 files changed, 195 insertions(+) create mode 100644 assets/test/ModularPackage/Module1/.vscode/launch.json create mode 100644 assets/test/ModularPackage/Module1/Package.swift create mode 100644 assets/test/ModularPackage/Module1/Sources/Module1/Module1.swift create mode 100644 assets/test/ModularPackage/Module1/Sources/Module1Demo/Module1Demo.swift create mode 100644 assets/test/ModularPackage/Module1/Tests/Module1Tests.swift create mode 100644 assets/test/ModularPackage/Module2/.vscode/launch.json create mode 100644 assets/test/ModularPackage/Module2/Package.swift create mode 100644 assets/test/ModularPackage/Module2/Sources/Module2/Module2.swift create mode 100644 assets/test/ModularPackage/Module2/Sources/Module2Demo/main.swift create mode 100644 assets/test/ModularPackage/Module2/Tests/Module2Tests.swift create mode 100644 assets/test/ModularPackage/Package.swift diff --git a/assets/test/ModularPackage/Module1/.vscode/launch.json b/assets/test/ModularPackage/Module1/.vscode/launch.json new file mode 100644 index 000000000..ec95fe6c1 --- /dev/null +++ b/assets/test/ModularPackage/Module1/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:Module1}", + "name": "Debug Module1Demo", + "program": "${workspaceFolder:Module1}/.build/debug/Module1Demo", + "preLaunchTask": "swift: Build Debug Module1Demo" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:Module1}", + "name": "Release Module1Demo", + "program": "${workspaceFolder:Module1}/.build/release/Module1Demo", + "preLaunchTask": "swift: Build Release Module1Demo" + } + ] +} diff --git a/assets/test/ModularPackage/Module1/Package.swift b/assets/test/ModularPackage/Module1/Package.swift new file mode 100644 index 000000000..871dbc947 --- /dev/null +++ b/assets/test/ModularPackage/Module1/Package.swift @@ -0,0 +1,23 @@ +// swift-tools-version:6.0 + +import PackageDescription + +internal let package = Package( + name: "Module1", + products: [ + .executable(name: "Module1Demo", targets: ["Module1Demo"]), + ], + targets: [ + .testTarget( + name: "Module1Tests", + dependencies: ["Module1"] + ), + .executableTarget( + name: "Module1Demo", + dependencies: ["Module1"] + ), + .target( + name: "Module1" + ), + ] +) diff --git a/assets/test/ModularPackage/Module1/Sources/Module1/Module1.swift b/assets/test/ModularPackage/Module1/Sources/Module1/Module1.swift new file mode 100644 index 000000000..32c3e62fa --- /dev/null +++ b/assets/test/ModularPackage/Module1/Sources/Module1/Module1.swift @@ -0,0 +1,7 @@ +public struct Module1 { + public init() {} + + public func add(_ x: Int, _ y: Int) -> Int { + x + y + } +} \ No newline at end of file diff --git a/assets/test/ModularPackage/Module1/Sources/Module1Demo/Module1Demo.swift b/assets/test/ModularPackage/Module1/Sources/Module1Demo/Module1Demo.swift new file mode 100644 index 000000000..941beed71 --- /dev/null +++ b/assets/test/ModularPackage/Module1/Sources/Module1Demo/Module1Demo.swift @@ -0,0 +1,11 @@ +import Module1 + +private let module = Module1() + +@MainActor +func check(_ x: Int, _ y: Int) { + print(module.add(x, y)) +} + +check(1, 2) +check(2, 3) diff --git a/assets/test/ModularPackage/Module1/Tests/Module1Tests.swift b/assets/test/ModularPackage/Module1/Tests/Module1Tests.swift new file mode 100644 index 000000000..e7df7b33c --- /dev/null +++ b/assets/test/ModularPackage/Module1/Tests/Module1Tests.swift @@ -0,0 +1,20 @@ +import XCTest +@testable import Module1 + +internal final class Module1Tests: XCTestCase { + private var sut: Module1! + + override internal func setUp() { + super.setUp() + sut = .init() + } + + override internal func tearDown() { + sut = nil + super.tearDown() + } + + internal func test_add_with1And2_shouldReturn3() { + XCTAssertEqual(sut.add(1, 2), 3) + } +} \ No newline at end of file diff --git a/assets/test/ModularPackage/Module2/.vscode/launch.json b/assets/test/ModularPackage/Module2/.vscode/launch.json new file mode 100644 index 000000000..c85de9936 --- /dev/null +++ b/assets/test/ModularPackage/Module2/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + "configurations": [ + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:Module1}", + "name": "Debug Module1Demo", + "program": "${workspaceFolder:Module1}/.build/debug/Module1Demo", + "preLaunchTask": "swift: Build Debug Module1Demo" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:Module1}", + "name": "Release Module1Demo", + "program": "${workspaceFolder:Module1}/.build/release/Module1Demo", + "preLaunchTask": "swift: Build Release Module1Demo" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:Module2}", + "name": "Debug Module2Demo", + "program": "${workspaceFolder:Module2}/.build/debug/Module2Demo", + "preLaunchTask": "swift: Build Debug Module2Demo" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:Module2}", + "name": "Release Module2Demo", + "program": "${workspaceFolder:Module2}/.build/release/Module2Demo", + "preLaunchTask": "swift: Build Release Module2Demo" + } + ] +} diff --git a/assets/test/ModularPackage/Module2/Package.swift b/assets/test/ModularPackage/Module2/Package.swift new file mode 100644 index 000000000..9ccde0e86 --- /dev/null +++ b/assets/test/ModularPackage/Module2/Package.swift @@ -0,0 +1,23 @@ +// swift-tools-version:6.0 + +import PackageDescription + +internal let package = Package( + name: "Module2", + products: [ + .executable(name: "Module2Demo", targets: ["Module2Demo"]), + ], + targets: [ + .testTarget( + name: "Module2Tests", + dependencies: ["Module2"] + ), + .executableTarget( + name: "Module2Demo", + dependencies: ["Module2"] + ), + .target( + name: "Module2" + ), + ] +) diff --git a/assets/test/ModularPackage/Module2/Sources/Module2/Module2.swift b/assets/test/ModularPackage/Module2/Sources/Module2/Module2.swift new file mode 100644 index 000000000..07541013e --- /dev/null +++ b/assets/test/ModularPackage/Module2/Sources/Module2/Module2.swift @@ -0,0 +1,7 @@ +public struct Module2 { + public init() {} + + public func subtract(_ x: Int, _ y: Int) -> Int { + x - y + } +} \ No newline at end of file diff --git a/assets/test/ModularPackage/Module2/Sources/Module2Demo/main.swift b/assets/test/ModularPackage/Module2/Sources/Module2Demo/main.swift new file mode 100644 index 000000000..6d33bf22a --- /dev/null +++ b/assets/test/ModularPackage/Module2/Sources/Module2Demo/main.swift @@ -0,0 +1,11 @@ +import Module2 + +private let module = Module2() + +@MainActor +func check(_ x: Int, _ y: Int) { + print(module.subtract(x, y)) +} + +check(1, 2) +check(2, 3) diff --git a/assets/test/ModularPackage/Module2/Tests/Module2Tests.swift b/assets/test/ModularPackage/Module2/Tests/Module2Tests.swift new file mode 100644 index 000000000..795376105 --- /dev/null +++ b/assets/test/ModularPackage/Module2/Tests/Module2Tests.swift @@ -0,0 +1,20 @@ +import XCTest +@testable import Module2 + +internal final class Module2Tests: XCTestCase { + private var sut: Module2! + + override internal func setUp() { + super.setUp() + sut = .init() + } + + override internal func tearDown() { + sut = nil + super.tearDown() + } + + internal func test_add_with5And2_shouldReturn3() { + XCTAssertEqual(sut.subtract(5, 2), 3) + } +} \ No newline at end of file diff --git a/assets/test/ModularPackage/Package.swift b/assets/test/ModularPackage/Package.swift new file mode 100644 index 000000000..c8af6a378 --- /dev/null +++ b/assets/test/ModularPackage/Package.swift @@ -0,0 +1,11 @@ +// swift-tools-version:6.0 + +import PackageDescription + +internal let package = Package( + name: "ModularPackage", + dependencies: [ + .package(path: "Module1"), + .package(path: "Module2"), + ] +) From e10ac2925a25b28ea9a3a8dc1ceb5bb8a7ebd2ea Mon Sep 17 00:00:00 2001 From: Ahmed AbdelMagied Date: Sun, 9 Mar 2025 13:25:56 +0200 Subject: [PATCH 3/5] Add search for subpackages tests --- test/unit-tests/utilities/workspace.test.ts | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/unit-tests/utilities/workspace.test.ts diff --git a/test/unit-tests/utilities/workspace.test.ts b/test/unit-tests/utilities/workspace.test.ts new file mode 100644 index 000000000..82093146c --- /dev/null +++ b/test/unit-tests/utilities/workspace.test.ts @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 { searchForPackages } from "../../../src/utilities/workspace"; +import { testAssetUri } from "../../fixtures"; +import { expect } from "chai"; + +suite("Workspace Utilities Unit Test Suite", () => { + suite("searchForPackages", () => { + const packageFolder = testAssetUri("ModularPackage"); + const firstModuleFolder = vscode.Uri.joinPath(packageFolder, "Module1"); + const secondModuleFolder = vscode.Uri.joinPath(packageFolder, "Module2"); + + test("returns only root package when search for subpackages disabled", async () => { + const folders = await searchForPackages(packageFolder, false, false); + + expect(folders.map(folder => folder.fsPath)).eql([packageFolder.fsPath]); + }); + + test("returns sub packages when search for subpackages enabled", async () => { + const folders = await searchForPackages(packageFolder, false, true); + + expect(folders.map(folder => folder.fsPath).sort()).deep.equal([ + packageFolder.fsPath, + firstModuleFolder.fsPath, + secondModuleFolder.fsPath, + ]); + }); + }); +}); From 944d96301cdae82b79b3dcb6eab1ed5bec72b74b Mon Sep 17 00:00:00 2001 From: Ahmed AbdelMagied Date: Sun, 9 Mar 2025 13:36:05 +0200 Subject: [PATCH 4/5] Fix extension failed to find subpackages within a package --- src/utilities/workspace.ts | 1 - test/unit-tests/utilities/workspace.test.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utilities/workspace.ts b/src/utilities/workspace.ts index 5fccec752..cc0a6fa69 100644 --- a/src/utilities/workspace.ts +++ b/src/utilities/workspace.ts @@ -26,7 +26,6 @@ export async function searchForPackages( // add folder if Package.swift/compile_commands.json/compile_flags.txt/buildServer.json exists if (await isValidWorkspaceFolder(folder.fsPath, disableSwiftPMIntegration)) { folders.push(folder); - return; } // should I search sub-folders for more Swift Packages if (!searchSubfoldersForPackages) { diff --git a/test/unit-tests/utilities/workspace.test.ts b/test/unit-tests/utilities/workspace.test.ts index 82093146c..7ca500fc0 100644 --- a/test/unit-tests/utilities/workspace.test.ts +++ b/test/unit-tests/utilities/workspace.test.ts @@ -29,7 +29,7 @@ suite("Workspace Utilities Unit Test Suite", () => { expect(folders.map(folder => folder.fsPath)).eql([packageFolder.fsPath]); }); - test("returns sub packages when search for subpackages enabled", async () => { + test("returns subpackages when search for subpackages enabled", async () => { const folders = await searchForPackages(packageFolder, false, true); expect(folders.map(folder => folder.fsPath).sort()).deep.equal([ From c013f901d2852638ffb83b40cf360dcc0aeb2358 Mon Sep 17 00:00:00 2001 From: Ahmed AbdelMagied Date: Mon, 10 Mar 2025 17:31:01 +0200 Subject: [PATCH 5/5] Remove auto generated launch.json files --- .../Module1/.vscode/launch.json | 22 ---------- .../Module2/.vscode/launch.json | 40 ------------------- 2 files changed, 62 deletions(-) delete mode 100644 assets/test/ModularPackage/Module1/.vscode/launch.json delete mode 100644 assets/test/ModularPackage/Module2/.vscode/launch.json diff --git a/assets/test/ModularPackage/Module1/.vscode/launch.json b/assets/test/ModularPackage/Module1/.vscode/launch.json deleted file mode 100644 index ec95fe6c1..000000000 --- a/assets/test/ModularPackage/Module1/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "configurations": [ - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:Module1}", - "name": "Debug Module1Demo", - "program": "${workspaceFolder:Module1}/.build/debug/Module1Demo", - "preLaunchTask": "swift: Build Debug Module1Demo" - }, - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:Module1}", - "name": "Release Module1Demo", - "program": "${workspaceFolder:Module1}/.build/release/Module1Demo", - "preLaunchTask": "swift: Build Release Module1Demo" - } - ] -} diff --git a/assets/test/ModularPackage/Module2/.vscode/launch.json b/assets/test/ModularPackage/Module2/.vscode/launch.json deleted file mode 100644 index c85de9936..000000000 --- a/assets/test/ModularPackage/Module2/.vscode/launch.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "configurations": [ - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:Module1}", - "name": "Debug Module1Demo", - "program": "${workspaceFolder:Module1}/.build/debug/Module1Demo", - "preLaunchTask": "swift: Build Debug Module1Demo" - }, - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:Module1}", - "name": "Release Module1Demo", - "program": "${workspaceFolder:Module1}/.build/release/Module1Demo", - "preLaunchTask": "swift: Build Release Module1Demo" - }, - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:Module2}", - "name": "Debug Module2Demo", - "program": "${workspaceFolder:Module2}/.build/debug/Module2Demo", - "preLaunchTask": "swift: Build Debug Module2Demo" - }, - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:Module2}", - "name": "Release Module2Demo", - "program": "${workspaceFolder:Module2}/.build/release/Module2Demo", - "preLaunchTask": "swift: Build Release Module2Demo" - } - ] -}