From 2bea2e667a5f59a76920624f3cc57355f961c1e4 Mon Sep 17 00:00:00 2001 From: Jeff Schmitz Date: Fri, 15 Aug 2025 11:06:26 -0500 Subject: [PATCH 1/2] Add SwiftTesting support for executable and tool packages Updated InitPackage to handle .executable and .tool types when generating test files with SwiftTesting. Added corresponding tests to verify correct test file content for these package types. --- Sources/Workspace/InitPackage.swift | 16 ++++++-- Tests/WorkspaceTests/InitTests.swift | 56 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/Sources/Workspace/InitPackage.swift b/Sources/Workspace/InitPackage.swift index f66d72c5985..f8fba9033b1 100644 --- a/Sources/Workspace/InitPackage.swift +++ b/Sources/Workspace/InitPackage.swift @@ -660,10 +660,16 @@ public final class InitPackage { return } + // Intentionally exhaustive switch without `default` case - forces compilation failure when new + // `PackageType`` cases are added, ensuring explicit consideration of case behavior. + // Using `default` would silently group future cases, hiding new cases and introducing potential bugs. switch packageType { - case .empty, .executable, .tool, .buildToolPlugin, .commandPlugin: return - default: break + case .empty, .buildToolPlugin, .commandPlugin: + return + case .library, .executable, .tool, .macro: + break } + let tests = destinationPath.appending("Tests") guard self.fileSystem.exists(tests) == false else { return @@ -873,9 +879,11 @@ public final class InitPackage { try makeDirectories(testModule) let testClassFile = try AbsolutePath(validating: "\(moduleName)Tests.swift", relativeTo: testModule) + switch packageType { - case .empty, .buildToolPlugin, .commandPlugin, .executable, .tool: break - case .library: + case .empty, .buildToolPlugin, .commandPlugin: + break + case .library, .executable, .tool: // Added .executable and .tool here try writeLibraryTestsFile(testClassFile) case .macro: try writeMacroTestsFile(testClassFile) diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index d09e117d1a5..287077570b6 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -297,6 +297,62 @@ final class InitTests: XCTestCase { } } + func testInitPackageExecutableWithSwiftTesting() async throws { + try testWithTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("Foo") + let name = path.basename + try fs.createDirectory(path) + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .executable, + supportedTestingLibraries: [.swiftTesting], + destinationPath: path, + fileSystem: localFileSystem + ) + + try initPackage.writePackageStructure() + // Verify basic file system content that we expect in the package + let manifest = path.appending("Package.swift") + XCTAssertFileExists(manifest) + let testFile = path.appending("Tests").appending("FooTests").appending("FooTests.swift") + let testFileContents: String = try localFileSystem.readFileContents(testFile) + XCTAssertMatch(testFileContents, .contains(#"import Testing"#)) + XCTAssertNoMatch(testFileContents, .contains(#"import XCTest"#)) + XCTAssertMatch(testFileContents, .contains(#"@Test func example() async throws"#)) + XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) + } + } + + func testInitPackageToolWithSwiftTesting() async throws { + try testWithTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("Foo") + let name = path.basename + try fs.createDirectory(path) + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .tool, + supportedTestingLibraries: [.swiftTesting], + destinationPath: path, + fileSystem: localFileSystem + ) + + try initPackage.writePackageStructure() + // Verify basic file system content that we expect in the package + let manifest = path.appending("Package.swift") + XCTAssertFileExists(manifest) + let testFile = path.appending("Tests").appending("FooTests").appending("FooTests.swift") + let testFileContents: String = try localFileSystem.readFileContents(testFile) + XCTAssertMatch(testFileContents, .contains(#"import Testing"#)) + XCTAssertNoMatch(testFileContents, .contains(#"import XCTest"#)) + XCTAssertMatch(testFileContents, .contains(#"@Test func example() async throws"#)) + XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) + } + } + func testInitPackageCommandPlugin() throws { try testWithTemporaryDirectory { tmpPath in let fs = localFileSystem From 66ce054e52129ad004279e828051dc8c031058df Mon Sep 17 00:00:00 2001 From: Jeff Schmitz Date: Wed, 20 Aug 2025 08:07:12 -0500 Subject: [PATCH 2/2] refactor: remove verbose exhaustive-switch comment The exhaustive-switch pattern is self-evident here and common across the codebase. This comment adds noise rather than clarity, so it's better removed and documented via a style guide if needed --- Sources/Workspace/InitPackage.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/Workspace/InitPackage.swift b/Sources/Workspace/InitPackage.swift index f8fba9033b1..79f8b8167fc 100644 --- a/Sources/Workspace/InitPackage.swift +++ b/Sources/Workspace/InitPackage.swift @@ -660,9 +660,6 @@ public final class InitPackage { return } - // Intentionally exhaustive switch without `default` case - forces compilation failure when new - // `PackageType`` cases are added, ensuring explicit consideration of case behavior. - // Using `default` would silently group future cases, hiding new cases and introducing potential bugs. switch packageType { case .empty, .buildToolPlugin, .commandPlugin: return @@ -883,7 +880,7 @@ public final class InitPackage { switch packageType { case .empty, .buildToolPlugin, .commandPlugin: break - case .library, .executable, .tool: // Added .executable and .tool here + case .library, .executable, .tool: try writeLibraryTestsFile(testClassFile) case .macro: try writeMacroTestsFile(testClassFile)