From 423353d9bf10ec35df5bcf861eaa1f0bac4b38d7 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 29 Aug 2025 16:33:27 -0400 Subject: [PATCH 01/12] [Tests] Convert InitTests to Swift Testing --- .../SwiftTesting+Helpers.swift | 44 +- Tests/WorkspaceTests/InitTests.swift | 714 ++++++++---------- 2 files changed, 361 insertions(+), 397 deletions(-) diff --git a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift index d7e3185b9a4..5a9ef7ae91f 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift @@ -11,30 +11,30 @@ import Basics import Testing +// MARK: File System Helpers + +/// Verifies that a file exists at the specified path. +/// +/// - Parameters: +/// - path: The absolute path to check for file existence. +/// - sourceLocation: The source location where the expectation is made. public func expectFileExists( at path: AbsolutePath, - _ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, ) { - let commentPrefix = - if let comment { - "\(comment): " - } else { - "" - } - let msgSuffix: String - do { - msgSuffix = try "Directory contents: \(localFileSystem.getDirectoryContents(path.parentDirectory))" - } catch { - msgSuffix = "" - } #expect( localFileSystem.exists(path), - "\(commentPrefix)File '\(path)' does not exist. \(msgSuffix)", + "Files '\(path)' does not exist.", sourceLocation: sourceLocation, ) } +/// Verifies that a file does not exist at the specified path. +/// +/// - Parameters: +/// - fixturePath: The absolute path to check for file non-existence. +/// - comment: An optional comment to include in the failure message. +/// - sourceLocation: The source location where the expectation is made. public func expectFileDoesNotExists( at path: AbsolutePath, _ comment: Comment? = nil, @@ -59,6 +59,12 @@ public func expectFileDoesNotExists( ) } +/// Verifies that a file exists and is executable at the specified path. +/// +/// - Parameters: +/// - fixturePath: The absolute path to check for executable file existence. +/// - comment: An optional comment to include in the failure message. +/// - sourceLocation: The source location where the expectation is made. public func expectFileIsExecutable( at fixturePath: AbsolutePath, _ comment: Comment? = nil, @@ -77,6 +83,11 @@ public func expectFileIsExecutable( ) } +/// Verifies that a directory exists at the specified path. +/// +/// - Parameters: +/// - path: The absolute path to check for directory existence. +/// - sourceLocation: The source location where the expectation is made. public func expectDirectoryExists( at path: AbsolutePath, sourceLocation: SourceLocation = #_sourceLocation, @@ -94,6 +105,11 @@ let msgSuffix: String ) } +/// Verifies that a directory does not exist at the specified path. +/// +/// - Parameters: +/// - path: The absolute path to check for directory non-existence. +/// - sourceLocation: The source location where the expectation is made. public func expectDirectoryDoesNotExist( at path: AbsolutePath, sourceLocation: SourceLocation = #_sourceLocation, diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index c5a6cc61319..7fb4a20e3bb 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -14,59 +14,71 @@ import Basics import _InternalTestSupport import PackageModel import Workspace -import XCTest - -final class InitTests: XCTestCase { - - // MARK: TSCBasic package creation for each package type. - - func testInitPackageEmpty() 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: .empty, - destinationPath: path, - fileSystem: localFileSystem - ) - var progressMessages = [String]() - initPackage.progressReporter = { message in - progressMessages.append(message) +import Testing +import SPMBuildCore + +/// Tests for the `InitPackage` functionality, which creates new Swift packages with different configurations. +struct InitTests { + /// The target triple for the current platform, used to locate build products. + static let targetTriple: Triple = { + do { + return try UserToolchain.default.targetTriple + } catch { + fatalError("Failed to determine target triple: \(error)") + } + }() + + // MARK: - Helper Methods + + /// Asserts that the package under test builds successfully. + public func expectBuilds( + _ path: AbsolutePath, + buildSystem: BuildSystemProvider.Kind, + configurations: Set = [.debug, .release], + extraArgs: [String] = [], + Xcc: [String] = [], + Xld: [String] = [], + Xswiftc: [String] = [], + env: Environment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + ) async { + for conf in configurations { + await #expect(throws: Never.self, sourceLocation: sourceLocation) { + try await executeSwiftBuild( + path, + configuration: conf, + extraArgs: extraArgs, + Xcc: Xcc, + Xld: Xld, + Xswiftc: Xswiftc, + env: env, + buildSystem: buildSystem + ) } - try initPackage.writePackageStructure() - - // Not picky about the specific progress messages, just checking that we got some. - XCTAssertGreaterThan(progressMessages.count, 0) - - // Verify basic file system content that we expect in the package - let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) - let version = InitPackage.newPackageToolsVersion - let versionSpecifier = "\(version.major).\(version.minor)" - XCTAssertMatch(manifestContents, .prefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) - XCTAssertMatch(manifestContents, .contains(packageWithNameOnly(named: name))) } } - func testInitPackageExecutable() async throws { - try await testWithTemporaryDirectory { tmpPath in + /// Creates a test package with the specified configuration and verifies its structure. + private func createAndVerifyPackage( + packageType: InitPackage.PackageType, + name: String = "Foo", + supportedTestingLibraries: Set = [.xctest], + buildSystem: BuildSystemProvider.Kind? = nil, + customVerification: ((AbsolutePath, String) throws -> Void)? = nil + ) async throws { + return try await testWithTemporaryDirectory { tmpPath in let fs = localFileSystem - let path = tmpPath.appending("Foo") - let name = path.basename + let path = tmpPath.appending(name) + let packageName = path.basename try fs.createDirectory(path) // Create the package let initPackage = try InitPackage( - name: name, - packageType: .executable, + name: packageName, + packageType: packageType, + supportedTestingLibraries: supportedTestingLibraries, destinationPath: path, - fileSystem: localFileSystem + fileSystem: fs ) var progressMessages = [String]() initPackage.progressReporter = { message in @@ -74,370 +86,292 @@ final class InitTests: XCTestCase { } try initPackage.writePackageStructure() - // Not picky about the specific progress messages, just checking that we got some. - XCTAssertGreaterThan(progressMessages.count, 0) - - // Verify basic file system content that we expect in the package + #expect(progressMessages.count > 0, "Expected progress messages during package creation") + let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) + expectFileExists(at: manifest) + + let manifestContents: String = try fs.readFileContents(manifest) let version = InitPackage.newPackageToolsVersion let versionSpecifier = "\(version.major).\(version.minor)" - XCTAssertMatch(manifestContents, .prefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) + #expect(manifestContents.hasPrefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) - XCTAssertEqual(try fs.getDirectoryContents(path.appending("Sources").appending("Foo")), ["Foo.swift"]) - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - let binPath = path.appending(components: ".build", triple.platformBuildPathComponent, "debug") -#if os(Windows) - XCTAssertFileExists(binPath.appending("Foo.exe")) -#else - XCTAssertFileExists(binPath.appending("Foo")) -#endif - XCTAssertFileExists(binPath.appending(components: "Modules", "Foo.swiftmodule")) - } - } + if !supportedTestingLibraries.isEmpty { + #expect(manifestContents.contains(".testTarget(")) + } - func testInitPackageExecutableCalledMain() async throws { - try await testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("main") - let name = path.basename - try fs.createDirectory(path) + try customVerification?(path, packageName) - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .executable, - destinationPath: path, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() + if let buildSystem = buildSystem { + await expectBuilds(path, buildSystem: buildSystem) - XCTAssertEqual(try fs.getDirectoryContents(path.appending("Sources").appending("main")), ["MainEntrypoint.swift"]) - await XCTAssertBuilds( - path, - buildSystem: .native, - ) + try verifyBuildProducts(for: packageType, at: path, name: packageName, buildSystem: buildSystem) + } } } - func testInitPackageLibraryWithXCTestOnly() async throws { - try await 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: .library, - supportedTestingLibraries: [.xctest], - destinationPath: path, - fileSystem: localFileSystem - ) - var progressMessages = [String]() - initPackage.progressReporter = { message in - progressMessages.append(message) + /// Verifies that the expected build products exist for a package. + private func verifyBuildProducts( + for packageType: InitPackage.PackageType, + at path: AbsolutePath, + name: String, + buildSystem: BuildSystemProvider.Kind + ) throws { + let expectedPath = path.appending(components: try buildSystem.binPath(for: BuildConfiguration.debug)) + + switch packageType { + case .library: + if buildSystem == .native { + expectFileExists(at: expectedPath.appending("Modules", "\(name).swiftmodule")) + } else { + expectFileExists(at: expectedPath.appending("\(name).swiftmodule")) } - try initPackage.writePackageStructure() - - // Not picky about the specific progress messages, just checking that we got some. - XCTAssertGreaterThan(progressMessages.count, 0) - - // Verify basic file system content that we expect in the package - let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) - let version = InitPackage.newPackageToolsVersion - let versionSpecifier = "\(version.major).\(version.minor)" - XCTAssertMatch(manifestContents, .prefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) - - XCTAssertEqual(try fs.getDirectoryContents(path.appending("Sources").appending("Foo")), ["Foo.swift"]) - - let tests = path.appending("Tests") - XCTAssertEqual(try fs.getDirectoryContents(tests).sorted(), ["FooTests"]) - - let testFile = tests.appending("FooTests").appending("FooTests.swift") - let testFileContents: String = try localFileSystem.readFileContents(testFile) - XCTAssertTrue(testFileContents.hasPrefix("import XCTest"), """ - Validates formatting of XCTest source file, in particular that it does not contain leading whitespace: - \(testFileContents) - """) - XCTAssertMatch(testFileContents, .contains("func testExample() throws")) - - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) + case .executable, .tool: + expectFileExists(at: expectedPath.appending(executableName(name))) + case .empty, .buildToolPlugin, .commandPlugin, .macro: + // These types don't have specific build products to verify or are verified separately + break } } - func testInitPackageLibraryWithSwiftTestingOnly() 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: .library, - supportedTestingLibraries: [.swiftTesting], - destinationPath: path, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() + /// Verifies the test file contents for a package. + private func verifyTestFileContents( + at path: AbsolutePath, + name: String, + hasSwiftTesting: Bool, + hasXCTest: Bool + ) throws { + let testFile = path.appending("Tests").appending("\(name)Tests").appending("\(name)Tests.swift") + let testFileContents: String = try localFileSystem.readFileContents(testFile) + + if hasSwiftTesting { + #expect(testFileContents.contains(#"import Testing"#)) + #expect(testFileContents.contains(#"@Test func example() async throws"#)) + } else { + #expect(!testFileContents.contains(#"import Testing"#)) + #expect(!testFileContents.contains(#"@Test func example() async throws"#)) + } - // 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")) - -#if canImport(TestingDisabled) - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) -#endif + if hasXCTest { + #expect(testFileContents.contains(#"import XCTest"#)) + #expect(testFileContents.contains("func testExample() throws")) + + if hasSwiftTesting { + // When both are present, ensure XCTest content is properly formatted + #expect(testFileContents.contains("import XCTest"), "XCTest import should be present") + } else { + // When only XCTest is present, ensure it's at the beginning of the file + #expect(testFileContents.hasPrefix("import XCTest"), """ + Validates formatting of XCTest source file, in particular that it does not contain leading whitespace: + \(testFileContents) + """) + } + } else { + #expect(!testFileContents.contains(#"import XCTest"#)) + #expect(!testFileContents.contains("func testExample() throws")) } } - func testInitPackageLibraryWithBothSwiftTestingAndXCTest() async throws { - try testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("Foo") - let name = path.basename - try fs.createDirectory(path) + /// Verifies plugin package contents. + private func verifyPluginPackage( + at path: AbsolutePath, + name: String, + isCommandPlugin: Bool + ) throws { + let manifest = path.appending("Package.swift") + expectFileExists(at: manifest) + let manifestContents: String = try localFileSystem.readFileContents(manifest) + + // Verify manifest contents + #expect(manifestContents.contains(".plugin(") && manifestContents.contains("targets: [\"\(name)\"]")) + + if isCommandPlugin { + #expect(manifestContents.contains(".plugin(") && + manifestContents.contains("capability: .command(intent: .custom(") && + manifestContents.contains("verb: \"\(name)\"")) + } else { + #expect(manifestContents.contains(".plugin(") && manifestContents.contains("capability: .buildTool()")) + } - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .library, - supportedTestingLibraries: [.swiftTesting, .xctest], - destinationPath: path, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() + // Verify source file + let source = path.appending("Plugins", "\(name).swift") + expectFileExists(at: source) + let sourceContents: String = try localFileSystem.readFileContents(source) + + if isCommandPlugin { + #expect(sourceContents.contains("struct \(name): CommandPlugin")) + #expect(sourceContents.contains("performCommand(context: PluginContext")) + } else { + #expect(sourceContents.contains("struct \(name): BuildToolPlugin")) + #expect(sourceContents.contains("createBuildCommands(context: PluginContext")) + } - // 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"#)) - XCTAssertMatch(testFileContents, .contains(#"import XCTest"#)) - XCTAssertMatch(testFileContents, .contains(#"@Test func example() async throws"#)) - XCTAssertMatch(testFileContents, .contains("func testExample() throws")) - -#if canImport(TestingDisabled) - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) -#endif + // Both plugin types should have Xcode extensions + #expect(sourceContents.contains("import XcodeProjectPlugin")) + if isCommandPlugin { + #expect(sourceContents.contains("extension \(name): XcodeCommandPlugin")) + #expect(sourceContents.contains("performCommand(context: XcodePluginContext")) + } else { + #expect(sourceContents.contains("extension \(name): XcodeBuildToolPlugin")) + #expect(sourceContents.contains("createBuildCommands(context: XcodePluginContext")) } } - func testInitPackageLibraryWithNoTests() 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: .library, - supportedTestingLibraries: [], - 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 manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertNoMatch(manifestContents, .contains(#".testTarget"#)) - - XCTAssertNoSuchPath(path.appending("Tests")) - -#if canImport(TestingDisabled) - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) -#endif + // MARK: - Package Type Tests + + /// Tests creating an empty package. + @Test func initPackageEmpty() throws { + Task { + try await createAndVerifyPackage( + packageType: .empty, + supportedTestingLibraries: [], + customVerification: { path, name in + let manifestContents: String = try localFileSystem.readFileContents(path.appending("Package.swift")) + #expect(manifestContents.contains(packageWithNameOnly(named: name))) + }) } } - 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 manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .contains(".testTarget(")) - 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")) - } + /// Tests creating an executable package with different build systems. + @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) + func initPackageExecutable(buildSystem: BuildSystemProvider.Kind) async throws { + try await createAndVerifyPackage( + packageType: .executable, + buildSystem: buildSystem, + customVerification: { path, name in + let directoryContents = try localFileSystem.getDirectoryContents(path.appending("Sources").appending(name)) + #expect(directoryContents == ["\(name).swift"]) + } + ) } - 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 - ) + /// Tests creating an executable package named "main". + @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) + func initPackageExecutableCalledMain(buildSystem: BuildSystemProvider.Kind) async throws { + try await createAndVerifyPackage( + packageType: .executable, + name: "main", + buildSystem: buildSystem, + customVerification: { path, _ in + let directoryContents = try localFileSystem.getDirectoryContents(path.appending("Sources").appending("main")) + #expect(directoryContents == ["MainEntrypoint.swift"]) + } + ) + } - try initPackage.writePackageStructure() - // Verify basic file system content that we expect in the package - let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .contains(".testTarget(")) - 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")) - } + /// Tests creating packages with XCTest only. + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) + func initPackageLibraryWithXCTestOnly(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + try await createAndVerifyPackage( + packageType: packageType, + supportedTestingLibraries: [.xctest], + buildSystem: buildSystem, + customVerification: { path, name in + #expect(try localFileSystem.getDirectoryContents(path.appending("Sources").appending(name)) == ["\(name).swift"], + "Expected single source file in Sources/\(name) directory") + + let tests = path.appending("Tests") + #expect(try localFileSystem.getDirectoryContents(tests).sorted() == ["\(name)Tests"], + "Expected single test directory") + + try verifyTestFileContents(at: path, name: name, hasSwiftTesting: false, hasXCTest: true) + } + ) } - func testInitPackageCommandPlugin() throws { - try testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("MyCommandPlugin") - let name = path.basename - try fs.createDirectory(path) + /// Tests creating packages with Swift Testing only. + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) + func initPackagesWithSwiftTestingOnly(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + try await createAndVerifyPackage( + packageType: packageType, + supportedTestingLibraries: [.swiftTesting], + buildSystem: buildSystem, + customVerification: { path, name in + try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: false) + + #if canImport(TestingDisabled) + let expectedPath = path.appending(components: ".build", Self.targetTriple.platformBuildPathComponent, "debug", "Modules", "\(name).swiftmodule") + expectFileExists(at: expectedPath) + #endif + } + ) + } - // Create the package - try InitPackage( - name: name, - packageType: .commandPlugin, - destinationPath: path, - fileSystem: localFileSystem - ).writePackageStructure() + /// Tests creating packages with both Swift Testing and XCTest. + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) + func initPackageWithBothSwiftTestingAndXCTest(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + try await createAndVerifyPackage( + packageType: packageType, + supportedTestingLibraries: [.swiftTesting, .xctest], + buildSystem: buildSystem, + customVerification: { path, name in + try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: true) + + #if canImport(TestingDisabled) + let expectedPath = path.appending(components: ".build", Self.targetTriple.platformBuildPathComponent, "debug", "Modules", "\(name).swiftmodule") + expectFileExists(at: expectedPath) + #endif + } + ) + } - // Verify basic file system content that we expect in the package - let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), .contains("targets: [\"MyCommandPlugin\"]"))) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), - .and(.contains("capability: .command(intent: .custom("), .contains("verb: \"MyCommandPlugin\"")))) - - // Check basic content that we expect in the plugin source file - let source = path.appending("Plugins", "MyCommandPlugin.swift") - XCTAssertFileExists(source) - let sourceContents: String = try localFileSystem.readFileContents(source) - XCTAssertMatch(sourceContents, .contains("struct MyCommandPlugin: CommandPlugin")) - XCTAssertMatch(sourceContents, .contains("performCommand(context: PluginContext")) - XCTAssertMatch(sourceContents, .contains("import XcodeProjectPlugin")) - XCTAssertMatch(sourceContents, .contains("extension MyCommandPlugin: XcodeCommandPlugin")) - XCTAssertMatch(sourceContents, .contains("performCommand(context: XcodePluginContext")) - } + /// Tests creating packages with no testing libraries. + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) + func initPackageWithNoTests(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + try await createAndVerifyPackage( + packageType: packageType, + supportedTestingLibraries: [], + buildSystem: buildSystem, + customVerification: { path, name in + let manifestContents: String = try localFileSystem.readFileContents(path.appending("Package.swift")) + #expect(!manifestContents.contains(#".testTarget"#)) + + expectDirectoryDoesNotExist(at: path.appending("Tests")) + + #if canImport(TestingDisabled) + let expectedPath = path.appending(components: ".build", Self.targetTriple.platformBuildPathComponent, "debug", "Modules", "\(name).swiftmodule") + expectFileExists(at: expectedPath) + #endif + } + ) } - - func testInitPackageBuildToolPlugin() throws { - try testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("MyBuildToolPlugin") - let name = path.basename - try fs.createDirectory(path) - // Create the package - try InitPackage( - name: name, - packageType: .buildToolPlugin, - destinationPath: path, - fileSystem: localFileSystem - ).writePackageStructure() + /// Tests creating a command plugin package. + @Test func initPackageCommandPlugin() async throws { + try await createAndVerifyPackage( + packageType: .commandPlugin, + name: "MyCommandPlugin", + supportedTestingLibraries: [], + customVerification: { path, name in + try verifyPluginPackage(at: path, name: name, isCommandPlugin: true) + } + ) + } - // Verify basic file system content that we expect in the package - let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), .contains("targets: [\"MyBuildToolPlugin\"]"))) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), .contains("capability: .buildTool()"))) - - // Check basic content that we expect in the plugin source file - let source = path.appending("Plugins", "MyBuildToolPlugin.swift") - XCTAssertFileExists(source) - let sourceContents: String = try localFileSystem.readFileContents(source) - XCTAssertMatch(sourceContents, .contains("struct MyBuildToolPlugin: BuildToolPlugin")) - XCTAssertMatch(sourceContents, .contains("createBuildCommands(context: PluginContext")) - XCTAssertMatch(sourceContents, .contains("import XcodeProjectPlugin")) - XCTAssertMatch(sourceContents, .contains("extension MyBuildToolPlugin: XcodeBuildToolPlugin")) - XCTAssertMatch(sourceContents, .contains("createBuildCommands(context: XcodePluginContext")) - } + /// Tests creating a build tool plugin package. + @Test func initPackageBuildToolPlugin() async throws { + try await createAndVerifyPackage( + packageType: .buildToolPlugin, + name: "MyBuildToolPlugin", + supportedTestingLibraries: [], + customVerification: { path, name in + try verifyPluginPackage(at: path, name: name, isCommandPlugin: false) + } + ) } - // MARK: Special case testing + // MARK: - Special Case Tests - func testInitPackageNonc99Directory() async throws { + /// Tests creating a package in a directory with a non-C99 compliant name. + @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) + func initPackageNonc99Directory(buildSystem: BuildSystemProvider.Kind) async throws { try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in - XCTAssertDirectoryExists(tempDirPath) - // Create a directory with non c99name. let packageRoot = tempDirPath.appending("some-package") let packageName = packageRoot.basename try localFileSystem.createDirectory(packageRoot) - XCTAssertDirectoryExists(packageRoot) - + expectDirectoryExists(at: packageRoot) + // Create the package let initPackage = try InitPackage( name: packageName, @@ -445,27 +379,32 @@ final class InitTests: XCTestCase { destinationPath: packageRoot, fileSystem: localFileSystem ) - initPackage.progressReporter = { message in } + initPackage.progressReporter = { _ in } try initPackage.writePackageStructure() // Try building it. - await XCTAssertBuilds( - packageRoot, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(packageRoot.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "some_package.swiftmodule")) + await expectBuilds(packageRoot, buildSystem: buildSystem) + + // Assert that the expected build products exist + let expectedPath = packageRoot.appending(components: try buildSystem.binPath(for: BuildConfiguration.debug)) + + // Verify the module name is properly mangled + if buildSystem == .native { + expectFileExists(at: expectedPath.appending("Modules", "some_package.swiftmodule")) + } else { + expectFileExists(at: expectedPath.appending("some_package.swiftmodule")) + } } } - - func testNonC99NameExecutablePackage() async throws { + + /// Tests creating a package with a non-C99 compliant name. + @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) + func nonC99NameExecutablePackage(buildSystem: BuildSystemProvider.Kind) async throws { try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in - XCTAssertDirectoryExists(tempDirPath) - let packageRoot = tempDirPath.appending("Foo") try localFileSystem.createDirectory(packageRoot) - XCTAssertDirectoryExists(packageRoot) - + expectDirectoryExists(at: packageRoot) + // Create package with non c99name. let initPackage = try InitPackage( name: "package-name", @@ -474,16 +413,15 @@ final class InitTests: XCTestCase { fileSystem: localFileSystem ) try initPackage.writePackageStructure() - - await XCTAssertBuilds( - packageRoot, - buildSystem: .native, - ) + + await expectBuilds(packageRoot, buildSystem: buildSystem) } } - func testPlatforms() throws { + /// Tests creating a package with custom platform requirements. + @Test func platforms() throws { try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in + // Define custom platform requirements var options = InitPackage.InitPackageOptions(packageType: .library, supportedTestingLibraries: []) options.platforms = [ .init(platform: .macOS, version: PlatformVersion("10.15")), @@ -496,6 +434,7 @@ final class InitTests: XCTestCase { try localFileSystem.removeFileTree(packageRoot) try localFileSystem.createDirectory(packageRoot) + // Create the package with custom options let initPackage = try InitPackage( name: "Foo", options: options, @@ -505,11 +444,17 @@ final class InitTests: XCTestCase { ) try initPackage.writePackageStructure() + // Verify platform requirements are correctly included in the manifest let contents: String = try localFileSystem.readFileContents(packageRoot.appending("Package.swift")) - XCTAssertMatch(contents, .contains(#"platforms: [.macOS(.v10_15), .iOS(.v12), .watchOS("2.1"), .tvOS("999.0")],"#)) + #expect(contents.contains(#"platforms: [.macOS(.v10_15), .iOS(.v12), .watchOS("2.1"), .tvOS("999.0")],"#)) } } + // MARK: - Helper Methods for Package Content + + /// Creates a simple package manifest with just the name. + /// - Parameter name: The name of the package + /// - Returns: A string containing the package manifest private func packageWithNameOnly(named name: String) -> String { return """ let package = Package( @@ -518,6 +463,9 @@ final class InitTests: XCTestCase { """ } + /// Creates a package manifest with name and dependencies section. + /// - Parameter name: The name of the package + /// - Returns: A string containing the package manifest private func packageWithNameAndDependencies(with name: String) -> String { return """ let package = Package( From 4a9afa143bd6010812219b2f42cdee98483966dc Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Sat, 30 Aug 2025 13:48:31 -0400 Subject: [PATCH 02/12] Fixup tests --- Tests/WorkspaceTests/InitTests.swift | 32 ++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 7fb4a20e3bb..6a1bed14891 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -262,10 +262,16 @@ struct InitTests { /// Tests creating packages with XCTest only. @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) func initPackageLibraryWithXCTestOnly(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + #if canImport(TestingDisabled) + let buildSys = buildSystem + #else + let buildSys: BuildSystemProvider.Kind? = nil + #endif + try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.xctest], - buildSystem: buildSystem, + buildSystem: buildSys, customVerification: { path, name in #expect(try localFileSystem.getDirectoryContents(path.appending("Sources").appending(name)) == ["\(name).swift"], "Expected single source file in Sources/\(name) directory") @@ -282,10 +288,16 @@ struct InitTests { /// Tests creating packages with Swift Testing only. @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) func initPackagesWithSwiftTestingOnly(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + #if canImport(TestingDisabled) + let buildSys = buildSystem + #else + let buildSys: BuildSystemProvider.Kind? = nil + #endif + try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.swiftTesting], - buildSystem: buildSystem, + buildSystem: buildSys, customVerification: { path, name in try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: false) @@ -300,10 +312,16 @@ struct InitTests { /// Tests creating packages with both Swift Testing and XCTest. @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) func initPackageWithBothSwiftTestingAndXCTest(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + #if canImport(TestingDisabled) + let buildSys = buildSystem + #else + let buildSys: BuildSystemProvider.Kind? = nil + #endif + try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.swiftTesting, .xctest], - buildSystem: buildSystem, + buildSystem: buildSys, customVerification: { path, name in try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: true) @@ -318,10 +336,16 @@ struct InitTests { /// Tests creating packages with no testing libraries. @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) func initPackageWithNoTests(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { + #if canImport(TestingDisabled) + let buildSys = buildSystem + #else + let buildSys: BuildSystemProvider.Kind? = nil + #endif + try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [], - buildSystem: buildSystem, + buildSystem: buildSys, customVerification: { path, name in let manifestContents: String = try localFileSystem.readFileContents(path.appending("Package.swift")) #expect(!manifestContents.contains(#".testTarget"#)) From b844c0380696894bb9cccea96743351952bb63e0 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 12 Sep 2025 08:50:44 -0400 Subject: [PATCH 03/12] Fixup rebase --- .../SwiftTesting+Helpers.swift | 84 +++++++++++-------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift index 5a9ef7ae91f..06bf53b76d0 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift @@ -20,11 +20,16 @@ import Testing /// - sourceLocation: The source location where the expectation is made. public func expectFileExists( at path: AbsolutePath, + _ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, ) { #expect( localFileSystem.exists(path), - "Files '\(path)' does not exist.", + wrapMessage( + "Files '\(path)' does not exist.", + comment: comment, + directoryPath: path.parentDirectory + ), sourceLocation: sourceLocation, ) } @@ -32,7 +37,7 @@ public func expectFileExists( /// Verifies that a file does not exist at the specified path. /// /// - Parameters: -/// - fixturePath: The absolute path to check for file non-existence. +/// - path: The absolute path to check for file non-existence. /// - comment: An optional comment to include in the failure message. /// - sourceLocation: The source location where the expectation is made. public func expectFileDoesNotExists( @@ -40,21 +45,13 @@ public func expectFileDoesNotExists( _ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, ) { - let commentPrefix = - if let comment { - "\(comment): " - } else { - "" - } - let msgSuffix: String - do { - msgSuffix = try "Directory contents: \(localFileSystem.getDirectoryContents(path.parentDirectory))" - } catch { - msgSuffix = "" - } #expect( !localFileSystem.exists(path), - "\(commentPrefix)File: '\(path)' was not expected to exist, but does.\(msgSuffix))", + wrapMessage( + "File: '\(path)' was not expected to exist, but does.", + comment: comment, + directoryPath: path.parentDirectory + ), sourceLocation: sourceLocation, ) } @@ -70,15 +67,9 @@ public func expectFileIsExecutable( _ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, ) { - let commentPrefix = - if let comment { - "\(comment): " - } else { - "" - } #expect( localFileSystem.isExecutableFile(fixturePath), - "\(commentPrefix)File '\(fixturePath)' expected to be executable, but is not.", + wrapMessage("File '\(fixturePath)' expected to be executable, but is not.", comment: comment), sourceLocation: sourceLocation, ) } @@ -90,17 +81,12 @@ public func expectFileIsExecutable( /// - sourceLocation: The source location where the expectation is made. public func expectDirectoryExists( at path: AbsolutePath, + _ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, ) { -let msgSuffix: String - do { - msgSuffix = try "Directory contents: \(localFileSystem.getDirectoryContents(path))" - } catch { - msgSuffix = "" - } #expect( localFileSystem.isDirectory(path), - "Expected directory doesn't exist: '\(path)'. \(msgSuffix)", + wrapMessage("Expected directory doesn't exist: '\(path)'", comment: comment, directoryPath: path), sourceLocation: sourceLocation, ) } @@ -112,21 +98,47 @@ let msgSuffix: String /// - sourceLocation: The source location where the expectation is made. public func expectDirectoryDoesNotExist( at path: AbsolutePath, + _ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, ) { - let msgSuffix: String - do { - msgSuffix = try "Directory contents: \(localFileSystem.getDirectoryContents(path))" - } catch { - msgSuffix = "" - } #expect( !localFileSystem.isDirectory(path), - "Directory exists unexpectedly: '\(path)'.\(msgSuffix)", + wrapMessage("Directory exists unexpectedly: '\(path)'", comment: comment, directoryPath: path), sourceLocation: sourceLocation, ) } +/// Wraps a message with an optional comment prefix and directory contents suffix. +/// +/// - Parameters: +/// - message: The base message to wrap. +/// - comment: An optional comment to prefix the message with. +/// - directoryPath: An optional path to a folder whose contents will be appended to the message. +/// - Returns: The formatted message with prefix and suffix. +private func wrapMessage( + _ message: Comment, + comment: Comment? = nil, + directoryPath: AbsolutePath? = nil +) -> Comment { + let commentPrefix = + if let comment { + "\(comment): " + } else { + "" + } + + var msgSuffix = "" + if let directoryPath { + do { + msgSuffix = try " Directory contents: \(localFileSystem.getDirectoryContents(directoryPath))" + } catch { + // Silently ignore errors when getting directory contents + } + } + + return "\(commentPrefix)\(message)\(msgSuffix)" +} + /// Expects that the expression throws a CommandExecutionError and passes it to the provided throwing error handler. /// - Parameters: /// - expression: The expression expected to throw From e7e73eeb4ed057832f40f886032f66b854a60b88 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 7 Oct 2025 14:59:32 -0400 Subject: [PATCH 04/12] Pass through function name to fix long path issues on windows --- Tests/WorkspaceTests/InitTests.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 6a1bed14891..260e77242ab 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -64,9 +64,10 @@ struct InitTests { name: String = "Foo", supportedTestingLibraries: Set = [.xctest], buildSystem: BuildSystemProvider.Kind? = nil, - customVerification: ((AbsolutePath, String) throws -> Void)? = nil + customVerification: ((AbsolutePath, String) throws -> Void)? = nil, + function: StaticString = #function ) async throws { - return try await testWithTemporaryDirectory { tmpPath in + return try await testWithTemporaryDirectory(function: function) { tmpPath in let fs = localFileSystem let path = tmpPath.appending(name) let packageName = path.basename From 7cf344793dc7be6ad65955f09c0890a6af7cd633 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 14 Oct 2025 13:40:30 -0400 Subject: [PATCH 05/12] Generate shorter tmp directory names on Windows --- Sources/_InternalTestSupport/misc.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/_InternalTestSupport/misc.swift b/Sources/_InternalTestSupport/misc.swift index 944a9b1c079..16303cfd7cc 100644 --- a/Sources/_InternalTestSupport/misc.swift +++ b/Sources/_InternalTestSupport/misc.swift @@ -97,7 +97,15 @@ public func testWithTemporaryDirectory( .replacing(")", with: "") .replacing(".", with: "") .replacing(":", with: "_") - return try await withTemporaryDirectory(prefix: "spm-tests-\(cleanedFunction)") { tmpDirPath in + + // Use shorter prefix on Windows to avoid MAX_PATH issues + #if os(Windows) + let prefix = "spm-\(abs(cleanedFunction.hashValue))" + #else + let prefix = "spm-tests-\(cleanedFunction)" + #endif + + return try await withTemporaryDirectory(prefix: prefix) { tmpDirPath in defer { // Unblock and remove the tmp dir on deinit. try? localFileSystem.chmod(.userWritable, path: tmpDirPath, options: [.recursive]) From d54b0362a655dbc8abe8234fb777c4d6ec139479 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Wed, 22 Oct 2025 14:31:56 -0400 Subject: [PATCH 06/12] Even shorter Windows tmp directory paths --- Sources/_InternalTestSupport/misc.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/_InternalTestSupport/misc.swift b/Sources/_InternalTestSupport/misc.swift index 16303cfd7cc..cc8aa3a338e 100644 --- a/Sources/_InternalTestSupport/misc.swift +++ b/Sources/_InternalTestSupport/misc.swift @@ -100,7 +100,7 @@ public func testWithTemporaryDirectory( // Use shorter prefix on Windows to avoid MAX_PATH issues #if os(Windows) - let prefix = "spm-\(abs(cleanedFunction.hashValue))" + let prefix = "spm-\(String(abs(cleanedFunction.hashValue)).prefix(5))" #else let prefix = "spm-tests-\(cleanedFunction)" #endif From cda003875f6e11a1a27e4063db50ed6ee50000e2 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 23 Oct 2025 10:49:25 -0400 Subject: [PATCH 07/12] Address review feedback --- .../SwiftTesting+Helpers.swift | 21 +++ Tests/WorkspaceTests/InitTests.swift | 165 ++++++++++-------- 2 files changed, 114 insertions(+), 72 deletions(-) diff --git a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift index 06bf53b76d0..de4f1096184 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift @@ -34,6 +34,27 @@ public func expectFileExists( ) } +/// Requires that a file exists at the specified path. +/// +/// - Parameters: +/// - path: The absolute path to check for file existence. +/// - sourceLocation: The source location where the expectation is made. +public func requireFileExists( + at path: AbsolutePath, + _ comment: Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, +) throws { + try #require( + localFileSystem.exists(path), + wrapMessage( + "Files '\(path)' does not exist.", + comment: comment, + directoryPath: path.parentDirectory + ), + sourceLocation: sourceLocation, + ) +} + /// Verifies that a file does not exist at the specified path. /// /// - Parameters: diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 260e77242ab..81128b09819 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -20,6 +20,8 @@ import SPMBuildCore /// Tests for the `InitPackage` functionality, which creates new Swift packages with different configurations. struct InitTests { /// The target triple for the current platform, used to locate build products. + /// We instantiate this once lazily because it is not thread safe, and multiple tests + /// running in parallel can cause a crash. static let targetTriple: Triple = { do { return try UserToolchain.default.targetTriple @@ -64,6 +66,7 @@ struct InitTests { name: String = "Foo", supportedTestingLibraries: Set = [.xctest], buildSystem: BuildSystemProvider.Kind? = nil, + buildConfiguration: BuildConfiguration = .debug, customVerification: ((AbsolutePath, String) throws -> Void)? = nil, function: StaticString = #function ) async throws { @@ -104,9 +107,9 @@ struct InitTests { try customVerification?(path, packageName) if let buildSystem = buildSystem { - await expectBuilds(path, buildSystem: buildSystem) + await expectBuilds(path, buildSystem: buildSystem, configurations: Set([buildConfiguration])) - try verifyBuildProducts(for: packageType, at: path, name: packageName, buildSystem: buildSystem) + try verifyBuildProducts(for: packageType, at: path, name: packageName, buildSystem: buildSystem, buildConfiguration: buildConfiguration) } } } @@ -116,9 +119,10 @@ struct InitTests { for packageType: InitPackage.PackageType, at path: AbsolutePath, name: String, - buildSystem: BuildSystemProvider.Kind + buildSystem: BuildSystemProvider.Kind, + buildConfiguration: BuildConfiguration ) throws { - let expectedPath = path.appending(components: try buildSystem.binPath(for: BuildConfiguration.debug)) + let expectedPath = path.appending(components: try buildSystem.binPath(for: buildConfiguration, triple: Self.targetTriple.platformBuildPathComponent)) switch packageType { case .library: @@ -130,7 +134,7 @@ struct InitTests { case .executable, .tool: expectFileExists(at: expectedPath.appending(executableName(name))) case .empty, .buildToolPlugin, .commandPlugin, .macro: - // These types don't have specific build products to verify or are verified separately + Issue.record("Only library, executable, and tool packages have specific build products to verify.") break } } @@ -147,15 +151,15 @@ struct InitTests { if hasSwiftTesting { #expect(testFileContents.contains(#"import Testing"#)) - #expect(testFileContents.contains(#"@Test func example() async throws"#)) + #expect(testFileContents.contains(#"@Test func"#)) } else { #expect(!testFileContents.contains(#"import Testing"#)) - #expect(!testFileContents.contains(#"@Test func example() async throws"#)) + #expect(!testFileContents.contains(#"@Test func"#)) } if hasXCTest { #expect(testFileContents.contains(#"import XCTest"#)) - #expect(testFileContents.contains("func testExample() throws")) + #expect(testFileContents.contains("func test")) if hasSwiftTesting { // When both are present, ensure XCTest content is properly formatted @@ -169,7 +173,7 @@ struct InitTests { } } else { #expect(!testFileContents.contains(#"import XCTest"#)) - #expect(!testFileContents.contains("func testExample() throws")) + #expect(!testFileContents.contains("func test")) } } @@ -180,23 +184,25 @@ struct InitTests { isCommandPlugin: Bool ) throws { let manifest = path.appending("Package.swift") - expectFileExists(at: manifest) + try requireFileExists(at: manifest) let manifestContents: String = try localFileSystem.readFileContents(manifest) // Verify manifest contents #expect(manifestContents.contains(".plugin(") && manifestContents.contains("targets: [\"\(name)\"]")) if isCommandPlugin { - #expect(manifestContents.contains(".plugin(") && - manifestContents.contains("capability: .command(intent: .custom(") && - manifestContents.contains("verb: \"\(name)\"")) + #expect( + manifestContents.contains(".plugin(") && + manifestContents.contains("capability: .command(intent: .custom(") && + manifestContents.contains("verb: \"\(name)\"") + ) } else { #expect(manifestContents.contains(".plugin(") && manifestContents.contains("capability: .buildTool()")) } // Verify source file let source = path.appending("Plugins", "\(name).swift") - expectFileExists(at: source) + try requireFileExists(at: source) let sourceContents: String = try localFileSystem.readFileContents(source) if isCommandPlugin { @@ -234,11 +240,12 @@ struct InitTests { } /// Tests creating an executable package with different build systems. - @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) - func initPackageExecutable(buildSystem: BuildSystemProvider.Kind) async throws { + @Test(arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms)) + func initPackageExecutable(data: BuildData) async throws { try await createAndVerifyPackage( packageType: .executable, - buildSystem: buildSystem, + buildSystem: data.buildSystem, + buildConfiguration: data.config, customVerification: { path, name in let directoryContents = try localFileSystem.getDirectoryContents(path.appending("Sources").appending(name)) #expect(directoryContents == ["\(name).swift"]) @@ -247,12 +254,13 @@ struct InitTests { } /// Tests creating an executable package named "main". - @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) - func initPackageExecutableCalledMain(buildSystem: BuildSystemProvider.Kind) async throws { + @Test(arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms)) + func initPackageExecutableCalledMain(data: BuildData) async throws { try await createAndVerifyPackage( packageType: .executable, name: "main", - buildSystem: buildSystem, + buildSystem: data.buildSystem, + buildConfiguration: data.config, customVerification: { path, _ in let directoryContents = try localFileSystem.getDirectoryContents(path.appending("Sources").appending("main")) #expect(directoryContents == ["MainEntrypoint.swift"]) @@ -261,25 +269,24 @@ struct InitTests { } /// Tests creating packages with XCTest only. - @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) - func initPackageLibraryWithXCTestOnly(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { - #if canImport(TestingDisabled) - let buildSys = buildSystem - #else - let buildSys: BuildSystemProvider.Kind? = nil - #endif - + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], getBuildData(for: SupportedBuildSystemOnAllPlatforms)) + func initPackageLibraryWithXCTestOnly(packageType: InitPackage.PackageType, data: BuildData) async throws { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.xctest], - buildSystem: buildSys, + buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildConfiguration: data.config, customVerification: { path, name in - #expect(try localFileSystem.getDirectoryContents(path.appending("Sources").appending(name)) == ["\(name).swift"], - "Expected single source file in Sources/\(name) directory") + #expect( + try localFileSystem.getDirectoryContents(path.appending("Sources").appending(name)) == ["\(name).swift"], + "Expected single source file in Sources/\(name) directory" + ) let tests = path.appending("Tests") - #expect(try localFileSystem.getDirectoryContents(tests).sorted() == ["\(name)Tests"], - "Expected single test directory") + #expect( + try localFileSystem.getDirectoryContents(tests).sorted() == ["\(name)Tests"], + "Expected single test directory" + ) try verifyTestFileContents(at: path, name: name, hasSwiftTesting: false, hasXCTest: true) } @@ -287,47 +294,49 @@ struct InitTests { } /// Tests creating packages with Swift Testing only. - @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) - func initPackagesWithSwiftTestingOnly(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { - #if canImport(TestingDisabled) - let buildSys = buildSystem - #else - let buildSys: BuildSystemProvider.Kind? = nil - #endif - + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], getBuildData(for: SupportedBuildSystemOnAllPlatforms)) + func initPackagesWithSwiftTestingOnly(packageType: InitPackage.PackageType, data: BuildData) async throws { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.swiftTesting], - buildSystem: buildSys, + buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildConfiguration: data.config, customVerification: { path, name in try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: false) #if canImport(TestingDisabled) - let expectedPath = path.appending(components: ".build", Self.targetTriple.platformBuildPathComponent, "debug", "Modules", "\(name).swiftmodule") - expectFileExists(at: expectedPath) + let expectedPath = path.appending(components: + ".build", + Self.targetTriple.platformBuildPathComponent, + "debug", + "Modules", + "\(name).swiftmodule" + ) + try requireFileExists(at: expectedPath) #endif } ) } /// Tests creating packages with both Swift Testing and XCTest. - @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) - func initPackageWithBothSwiftTestingAndXCTest(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { - #if canImport(TestingDisabled) - let buildSys = buildSystem - #else - let buildSys: BuildSystemProvider.Kind? = nil - #endif - + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], getBuildData(for: SupportedBuildSystemOnAllPlatforms)) + func initPackageWithBothSwiftTestingAndXCTest(packageType: InitPackage.PackageType, data: BuildData) async throws { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.swiftTesting, .xctest], - buildSystem: buildSys, + buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildConfiguration: data.config, customVerification: { path, name in try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: true) #if canImport(TestingDisabled) - let expectedPath = path.appending(components: ".build", Self.targetTriple.platformBuildPathComponent, "debug", "Modules", "\(name).swiftmodule") + let expectedPath = path.appending(components: + ".build", + Self.targetTriple.platformBuildPathComponent, + "debug", + "Modules", + "\(name).swiftmodule" + ) expectFileExists(at: expectedPath) #endif } @@ -335,18 +344,13 @@ struct InitTests { } /// Tests creating packages with no testing libraries. - @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], [BuildSystemProvider.Kind.native, .swiftbuild]) - func initPackageWithNoTests(packageType: InitPackage.PackageType, buildSystem: BuildSystemProvider.Kind) async throws { - #if canImport(TestingDisabled) - let buildSys = buildSystem - #else - let buildSys: BuildSystemProvider.Kind? = nil - #endif - + @Test(arguments: [InitPackage.PackageType.library, .executable, .tool], getBuildData(for: SupportedBuildSystemOnAllPlatforms)) + func initPackageWithNoTests(packageType: InitPackage.PackageType, data: BuildData) async throws { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [], - buildSystem: buildSys, + buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildConfiguration: data.config, customVerification: { path, name in let manifestContents: String = try localFileSystem.readFileContents(path.appending("Package.swift")) #expect(!manifestContents.contains(#".testTarget"#)) @@ -354,7 +358,13 @@ struct InitTests { expectDirectoryDoesNotExist(at: path.appending("Tests")) #if canImport(TestingDisabled) - let expectedPath = path.appending(components: ".build", Self.targetTriple.platformBuildPathComponent, "debug", "Modules", "\(name).swiftmodule") + let expectedPath = path.appending(components: + ".build", + Self.targetTriple.platformBuildPathComponent, + "debug", + "Modules", + "\(name).swiftmodule" + ) expectFileExists(at: expectedPath) #endif } @@ -388,8 +398,8 @@ struct InitTests { // MARK: - Special Case Tests /// Tests creating a package in a directory with a non-C99 compliant name. - @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) - func initPackageNonc99Directory(buildSystem: BuildSystemProvider.Kind) async throws { + @Test(arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms)) + func initPackageNonc99Directory(data: BuildData) async throws { try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in // Create a directory with non c99name. let packageRoot = tempDirPath.appending("some-package") @@ -408,16 +418,17 @@ struct InitTests { try initPackage.writePackageStructure() // Try building it. - await expectBuilds(packageRoot, buildSystem: buildSystem) + await expectBuilds(packageRoot, buildSystem: data.buildSystem, configurations: [data.config]) // Assert that the expected build products exist - let expectedPath = packageRoot.appending(components: try buildSystem.binPath(for: BuildConfiguration.debug)) + let binPath = try data.buildSystem.binPath(for: data.config, triple: Self.targetTriple.platformBuildPathComponent) + let expectedPath = packageRoot.appending(components: binPath) // Verify the module name is properly mangled - if buildSystem == .native { - expectFileExists(at: expectedPath.appending("Modules", "some_package.swiftmodule")) - } else { - expectFileExists(at: expectedPath.appending("some_package.swiftmodule")) + switch data.buildSystem { + case .native: expectFileExists(at: expectedPath.appending("Modules", "some_package.swiftmodule")) + case .swiftbuild: expectFileExists(at: expectedPath.appending("some_package.swiftmodule")) + case .xcode: Issue.record("Not implemented") } } } @@ -502,4 +513,14 @@ let package = Package( ) """ } + + /// Returns the build system to use, respecting whether TestingDisabled is imported. + func buildSystemRespectingTestingDisabled(_ buildSystem: BuildSystemProvider.Kind?) -> BuildSystemProvider.Kind? { + #if canImport(TestingDisabled) + return buildSystem + #else + return nil + #endif + } + } From fbb80dd8d6d37f34871180e9fac1e67ea116a448 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 23 Oct 2025 11:06:46 -0400 Subject: [PATCH 08/12] Even shorter prefix for windows tmp dirs --- Sources/_InternalTestSupport/misc.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/_InternalTestSupport/misc.swift b/Sources/_InternalTestSupport/misc.swift index cc8aa3a338e..0b114951cc6 100644 --- a/Sources/_InternalTestSupport/misc.swift +++ b/Sources/_InternalTestSupport/misc.swift @@ -100,7 +100,7 @@ public func testWithTemporaryDirectory( // Use shorter prefix on Windows to avoid MAX_PATH issues #if os(Windows) - let prefix = "spm-\(String(abs(cleanedFunction.hashValue)).prefix(5))" + let prefix = "spm-" #else let prefix = "spm-tests-\(cleanedFunction)" #endif From d76b0082e55e9b3f24ca0c7027b4ef418bbee8f4 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 23 Oct 2025 11:15:43 -0400 Subject: [PATCH 09/12] Warn when we fallback to the default build system --- Tests/WorkspaceTests/InitTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 81128b09819..17a33a0cd99 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -519,6 +519,7 @@ let package = Package( #if canImport(TestingDisabled) return buildSystem #else + Issue.record("TestingDisabled was not imported, falling back to default buildSystem", severity: .warning) return nil #endif } From be9d84b45dbbfe21873bdbccf47c47a4593408e9 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 23 Oct 2025 13:08:27 -0400 Subject: [PATCH 10/12] Remove issue warning since it isn't supported in CI --- Tests/WorkspaceTests/InitTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 17a33a0cd99..81128b09819 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -519,7 +519,6 @@ let package = Package( #if canImport(TestingDisabled) return buildSystem #else - Issue.record("TestingDisabled was not imported, falling back to default buildSystem", severity: .warning) return nil #endif } From 68fa0ab2f39e2a7cc852b35d16302803c796a086 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 24 Oct 2025 09:18:56 -0400 Subject: [PATCH 11/12] Shorten nonC99NameExecutablePackage package name --- Tests/WorkspaceTests/InitTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 81128b09819..3c503edf9c9 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -443,7 +443,7 @@ struct InitTests { // Create package with non c99name. let initPackage = try InitPackage( - name: "package-name", + name: "Foo", packageType: .executable, destinationPath: packageRoot, fileSystem: localFileSystem From 7ffd7f49f8b8c2bf34fe626a408d4354ac2d3c41 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 24 Oct 2025 14:53:41 -0400 Subject: [PATCH 12/12] Cleanup testing assertions and canImport(TestingDisabled) --- .../BuildSystemProvider+Supported.swift | 7 ++ .../SwiftTesting+Helpers.swift | 2 +- Tests/CommandsTests/PackageCommandTests.swift | 14 ++-- Tests/WorkspaceTests/InitTests.swift | 84 +++++-------------- 4 files changed, 38 insertions(+), 69 deletions(-) diff --git a/Sources/_InternalTestSupport/BuildSystemProvider+Supported.swift b/Sources/_InternalTestSupport/BuildSystemProvider+Supported.swift index 3258f8caf6c..b068ecf8444 100644 --- a/Sources/_InternalTestSupport/BuildSystemProvider+Supported.swift +++ b/Sources/_InternalTestSupport/BuildSystemProvider+Supported.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import Testing import struct SPMBuildCore.BuildSystemProvider import enum PackageModel.BuildConfiguration @@ -28,6 +29,12 @@ public struct BuildData { public let config: BuildConfiguration } +extension BuildData: CustomTestStringConvertible { + public var testDescription: String { + return "\(buildSystem):\(config)" + } +} + public func getBuildData(for buildSystems: [BuildSystemProvider.Kind]) -> [BuildData] { buildSystems.flatMap { buildSystem in BuildConfiguration.allCases.compactMap { config in diff --git a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift index de4f1096184..2c7902968dd 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift @@ -61,7 +61,7 @@ public func requireFileExists( /// - path: The absolute path to check for file non-existence. /// - comment: An optional comment to include in the failure message. /// - sourceLocation: The source location where the expectation is made. -public func expectFileDoesNotExists( +public func expectFileDoesNotExist( at path: AbsolutePath, _ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index e7b1936c4b5..84879c3bc0a 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -2866,7 +2866,7 @@ struct PackageCommandTests { configuration: data.config, buildSystem: data.buildSystem, ) - expectFileDoesNotExists(at: binFile) + expectFileDoesNotExist(at: binFile) // Clean again to ensure we get no error. _ = try await execute( ["clean"], @@ -2911,7 +2911,7 @@ struct PackageCommandTests { configuration: data.config, buildSystem: data.buildSystem, ) - expectFileDoesNotExists(at: binFile) + expectFileDoesNotExist(at: binFile) try #expect( !localFileSystem.getDirectoryContents(buildPath.appending("repositories")).isEmpty ) @@ -3014,7 +3014,7 @@ struct PackageCommandTests { #expect(!result.stderr.contains("Could not find Package.swift")) // Verify manifest.db was removed (the purge implementation removes this file) - expectFileDoesNotExists(at: manifestDB, "manifest.db should be removed after purge") + expectFileDoesNotExist(at: manifestDB, "manifest.db should be removed after purge") // Note: SQLite auxiliary files (WAL/SHM) may or may not be removed depending on SQLite state // The important check is that the main database file is removed @@ -5341,7 +5341,7 @@ struct PackageCommandTests { buildSystem: buildData.buildSystem, ) expectFileIsExecutable(at: fixturePath.appending(components: debugTarget), "build-target") - expectFileDoesNotExists( + expectFileDoesNotExist( at: fixturePath.appending(components: releaseTarget), "build-target build-inherit" ) @@ -5375,7 +5375,7 @@ struct PackageCommandTests { at: fixturePath.appending(components: debugTarget), "build-target build-debug" ) - expectFileDoesNotExists( + expectFileDoesNotExist( at: fixturePath.appending(components: releaseTarget), "build-target build-inherit" ) @@ -5406,7 +5406,7 @@ struct PackageCommandTests { configuration: buildData.config, buildSystem: buildData.buildSystem, ) - expectFileDoesNotExists( + expectFileDoesNotExist( at: fixturePath.appending(components: debugTarget), "build-target build-inherit" ) @@ -5450,7 +5450,7 @@ struct PackageCommandTests { fileShouldNotExist = fixturePath.appending(components: debugTarget) fileShouldExist = fixturePath.appending(components: releaseTarget) } - expectFileDoesNotExists(at: fileShouldNotExist, "build-target build-inherit") + expectFileDoesNotExist(at: fileShouldNotExist, "build-target build-inherit") expectFileIsExecutable(at: fileShouldExist, "build-target build-inherit") } } when: { diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 3c503edf9c9..b3d890a913e 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -104,13 +104,13 @@ struct InitTests { #expect(manifestContents.contains(".testTarget(")) } - try customVerification?(path, packageName) - if let buildSystem = buildSystem { await expectBuilds(path, buildSystem: buildSystem, configurations: Set([buildConfiguration])) try verifyBuildProducts(for: packageType, at: path, name: packageName, buildSystem: buildSystem, buildConfiguration: buildConfiguration) } + + try customVerification?(path, packageName) } } @@ -152,28 +152,11 @@ struct InitTests { if hasSwiftTesting { #expect(testFileContents.contains(#"import Testing"#)) #expect(testFileContents.contains(#"@Test func"#)) - } else { - #expect(!testFileContents.contains(#"import Testing"#)) - #expect(!testFileContents.contains(#"@Test func"#)) } if hasXCTest { #expect(testFileContents.contains(#"import XCTest"#)) #expect(testFileContents.contains("func test")) - - if hasSwiftTesting { - // When both are present, ensure XCTest content is properly formatted - #expect(testFileContents.contains("import XCTest"), "XCTest import should be present") - } else { - // When only XCTest is present, ensure it's at the beginning of the file - #expect(testFileContents.hasPrefix("import XCTest"), """ - Validates formatting of XCTest source file, in particular that it does not contain leading whitespace: - \(testFileContents) - """) - } - } else { - #expect(!testFileContents.contains(#"import XCTest"#)) - #expect(!testFileContents.contains("func test")) } } @@ -274,7 +257,7 @@ struct InitTests { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.xctest], - buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildSystem: data.buildSystem, buildConfiguration: data.config, customVerification: { path, name in #expect( @@ -299,21 +282,17 @@ struct InitTests { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.swiftTesting], - buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildSystem: data.buildSystem, buildConfiguration: data.config, customVerification: { path, name in try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: false) + let binPath = try data.buildSystem.binPath(for: data.config, triple: Self.targetTriple.platformBuildPathComponent) + let swiftModule = "\(name).swiftmodule" + let expectedPath = path + .appending(components: binPath) + .appending(components: data.buildSystem == .native ? ["Modules", swiftModule] : [swiftModule]) - #if canImport(TestingDisabled) - let expectedPath = path.appending(components: - ".build", - Self.targetTriple.platformBuildPathComponent, - "debug", - "Modules", - "\(name).swiftmodule" - ) - try requireFileExists(at: expectedPath) - #endif + expectFileExists(at: expectedPath) } ) } @@ -324,21 +303,17 @@ struct InitTests { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [.swiftTesting, .xctest], - buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildSystem: data.buildSystem, buildConfiguration: data.config, customVerification: { path, name in try verifyTestFileContents(at: path, name: name, hasSwiftTesting: true, hasXCTest: true) + let binPath = try data.buildSystem.binPath(for: data.config, triple: Self.targetTriple.platformBuildPathComponent) + let swiftModule = "\(name).swiftmodule" + let expectedPath = path + .appending(components: binPath) + .appending(components: data.buildSystem == .native ? ["Modules", swiftModule] : [swiftModule]) - #if canImport(TestingDisabled) - let expectedPath = path.appending(components: - ".build", - Self.targetTriple.platformBuildPathComponent, - "debug", - "Modules", - "\(name).swiftmodule" - ) expectFileExists(at: expectedPath) - #endif } ) } @@ -349,7 +324,7 @@ struct InitTests { try await createAndVerifyPackage( packageType: packageType, supportedTestingLibraries: [], - buildSystem: buildSystemRespectingTestingDisabled(data.buildSystem), + buildSystem: data.buildSystem, buildConfiguration: data.config, customVerification: { path, name in let manifestContents: String = try localFileSystem.readFileContents(path.appending("Package.swift")) @@ -357,16 +332,13 @@ struct InitTests { expectDirectoryDoesNotExist(at: path.appending("Tests")) - #if canImport(TestingDisabled) - let expectedPath = path.appending(components: - ".build", - Self.targetTriple.platformBuildPathComponent, - "debug", - "Modules", - "\(name).swiftmodule" - ) + let binPath = try data.buildSystem.binPath(for: data.config, triple: Self.targetTriple.platformBuildPathComponent) + let swiftModule = "\(name).swiftmodule" + let expectedPath = path + .appending(components: binPath) + .appending(components: data.buildSystem == .native ? ["Modules", swiftModule] : [swiftModule]) + expectFileExists(at: expectedPath) - #endif } ) } @@ -513,14 +485,4 @@ let package = Package( ) """ } - - /// Returns the build system to use, respecting whether TestingDisabled is imported. - func buildSystemRespectingTestingDisabled(_ buildSystem: BuildSystemProvider.Kind?) -> BuildSystemProvider.Kind? { - #if canImport(TestingDisabled) - return buildSystem - #else - return nil - #endif - } - }