From f05100487a338166560061587edad9d4ffcab0e3 Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Mon, 15 Sep 2025 21:44:53 -0400 Subject: [PATCH 1/2] Add SwiftBuild coverage support Ensure the SwiftBuild build systam has feature parity with the Native build system as it relates to coverage. Fixes: #9077 Fixes: #9197 Issue: rdar://159461439 --- Fixtures/Coverage/Simple/.gitignore | 8 + Fixtures/Coverage/Simple/Package.swift | 26 + .../Simple/Sources/Simple/Simple.swift | 10 + .../Tests/SimpleTests/SimpleTests.swift | 24 + Sources/Basics/FileSystem/AbsolutePath.swift | 9 +- Sources/Basics/FileSystem/RelativePath.swift | 6 +- Sources/Commands/SwiftTestCommand.swift | 32 +- .../SwiftBuildSupport/SwiftBuildSystem.swift | 4 +- .../SwiftTesting+TraitsBug.swift | 7 + Tests/BasicsTests/FileSystem/PathTests.swift | 185 +++- Tests/BasicsTests/HTTPClientTests.swift | 14 +- Tests/BuildTests/PluginsBuildPlanTests.swift | 2 +- Tests/CommandsTests/BuildCommandTests.swift | 964 +++++++++--------- Tests/CommandsTests/CoverageTests.swift | 172 ++++ Tests/CommandsTests/RunCommandTests.swift | 12 +- .../Sanitizer+ExtensionsTests.swift | 33 + Tests/CommandsTests/TestCommandTests.swift | 2 +- Tests/FunctionalTests/PluginTests.swift | 28 +- Tests/FunctionalTests/TraitTests.swift | 36 +- Tests/IntegrationTests/SwiftPMTests.swift | 107 +- .../PackageGraphTests/ModulesGraphTests.swift | 2 +- 21 files changed, 1114 insertions(+), 569 deletions(-) create mode 100644 Fixtures/Coverage/Simple/.gitignore create mode 100644 Fixtures/Coverage/Simple/Package.swift create mode 100644 Fixtures/Coverage/Simple/Sources/Simple/Simple.swift create mode 100644 Fixtures/Coverage/Simple/Tests/SimpleTests/SimpleTests.swift create mode 100644 Tests/CommandsTests/CoverageTests.swift create mode 100644 Tests/CommandsTests/Sanitizer+ExtensionsTests.swift diff --git a/Fixtures/Coverage/Simple/.gitignore b/Fixtures/Coverage/Simple/.gitignore new file mode 100644 index 00000000000..0023a534063 --- /dev/null +++ b/Fixtures/Coverage/Simple/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Fixtures/Coverage/Simple/Package.swift b/Fixtures/Coverage/Simple/Package.swift new file mode 100644 index 00000000000..4449f7edfff --- /dev/null +++ b/Fixtures/Coverage/Simple/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Simple", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "Simple", + targets: ["Simple"] + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "Simple" + ), + .testTarget( + name: "SimpleTests", + dependencies: ["Simple"] + ), + ] +) diff --git a/Fixtures/Coverage/Simple/Sources/Simple/Simple.swift b/Fixtures/Coverage/Simple/Sources/Simple/Simple.swift new file mode 100644 index 00000000000..5e1e981ad5d --- /dev/null +++ b/Fixtures/Coverage/Simple/Sources/Simple/Simple.swift @@ -0,0 +1,10 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +public func greet(name: String = "world") -> String { + return "Hello, \(name)!" +} + +public func libA() -> String { + return "libA" +} diff --git a/Fixtures/Coverage/Simple/Tests/SimpleTests/SimpleTests.swift b/Fixtures/Coverage/Simple/Tests/SimpleTests/SimpleTests.swift new file mode 100644 index 00000000000..89e144011f1 --- /dev/null +++ b/Fixtures/Coverage/Simple/Tests/SimpleTests/SimpleTests.swift @@ -0,0 +1,24 @@ +import Testing +import XCTest +@testable import Simple + +@Test( + arguments: [ + "Bob", + "Alice", + "", + ] +) + func testGreet( + name: String + ) async throws { + let actual = greet(name: name) + + #expect(actual == "Hello, \(name)!") +} + +final class SimpleTests: XCTestCase { + func testExample() throws { + XCTAssertEqual(libA(), "libA", "Actual is not as expected") + } +} diff --git a/Sources/Basics/FileSystem/AbsolutePath.swift b/Sources/Basics/FileSystem/AbsolutePath.swift index c472e5c0608..23d5172fe02 100644 --- a/Sources/Basics/FileSystem/AbsolutePath.swift +++ b/Sources/Basics/FileSystem/AbsolutePath.swift @@ -53,14 +53,19 @@ public struct AbsolutePath: Hashable, Sendable { /// The input string will be normalized if needed, as described in the /// documentation for AbsolutePath. public init(validating pathString: String) throws { - self.underlying = try .init(validating: pathString) + self.underlying = try .init( + validating: pathString.trimmingCharacters(in: .whitespacesAndNewlines), + ) } /// Initializes an AbsolutePath from a string that may be either absolute /// or relative; if relative, `basePath` is used as the anchor; if absolute, /// it is used as is, and in this case `basePath` is ignored. public init(validating pathString: String, relativeTo basePath: AbsolutePath) throws { - self.underlying = try .init(validating: pathString, relativeTo: basePath.underlying) + self.underlying = try .init( + validating: pathString.trimmingCharacters(in: .whitespacesAndNewlines), + relativeTo: basePath.underlying, + ) } /// Initializes the AbsolutePath by concatenating a relative path to an diff --git a/Sources/Basics/FileSystem/RelativePath.swift b/Sources/Basics/FileSystem/RelativePath.swift index 38af45df6cd..5ec2b48b37a 100644 --- a/Sources/Basics/FileSystem/RelativePath.swift +++ b/Sources/Basics/FileSystem/RelativePath.swift @@ -9,7 +9,7 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// - +import Foundation import struct TSCBasic.RelativePath // public for transition @@ -40,7 +40,9 @@ public struct RelativePath: Hashable, Sendable { /// Convenience initializer that verifies that the path is relative. public init(validating pathString: String) throws { - self.underlying = try .init(validating: pathString) + self.underlying = try .init( + validating: pathString.trimmingCharacters(in: .whitespacesAndNewlines), + ) } /// Directory component. For a relative path without any path separators, diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 669f04dab3b..8dbf0d632a3 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -14,6 +14,7 @@ import ArgumentParser @_spi(SwiftPMInternal) import Basics +import struct Basics.Triple import _Concurrency @@ -597,7 +598,11 @@ public struct SwiftTestCommand: AsyncSwiftCommand { for product in testProducts { // Export the codecov data as JSON. let jsonPath = productsBuildParameters.codeCovAsJSONPath(packageName: rootManifest.displayName) - try await exportCodeCovAsJSON(to: jsonPath, testBinary: product.binaryPath, swiftCommandState: swiftCommandState) + try await exportCodeCovAsJSON( + to: jsonPath, + testBinary: product.binaryPath, + swiftCommandState: swiftCommandState, + ) } } @@ -619,7 +624,6 @@ public struct SwiftTestCommand: AsyncSwiftCommand { } } args += ["-o", productsBuildParameters.codeCovDataFile.pathString] - try await AsyncProcess.checkNonZeroExit(arguments: args) } @@ -632,11 +636,18 @@ public struct SwiftTestCommand: AsyncSwiftCommand { // Export using the llvm-cov tool. let llvmCov = try swiftCommandState.getTargetToolchain().getLLVMCov() let (productsBuildParameters, _) = try swiftCommandState.buildParametersForTest(options: self.options) + let archArgs: [String] = if let arch = productsBuildParameters.triple.llvmCovArchArgument { + // let archArgs: [String] = if let arch = productsBuildParameters.triple.arch { + ["--arch", "\(arch)"] + } else { + [] + } let args = [ llvmCov.pathString, "export", "-instr-profile=\(productsBuildParameters.codeCovDataFile)", - testBinary.pathString + ] + archArgs + [ + testBinary.pathString, ] let result = try await AsyncProcess.popen(arguments: args) @@ -709,6 +720,21 @@ extension SwiftTestCommand { } } +fileprivate extension Triple { + var llvmCovArchArgument: String? { + guard let arch = self.arch else { + return nil + } + switch arch { + case .aarch64: + // macOS uses arm64, Linux might use aarch64 + return ProcessInfo.hostOperatingSystem == .macOS ? "arm64" : "aarch64" + default: + return "\(arch)" + } + } +} + extension SwiftTestCommand { struct Last: SwiftCommand { @OptionGroup(visibility: .hidden) diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index 46a8c508300..0cf816656d6 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -1077,8 +1077,8 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { private static func constructTestingSettingsOverrides(from parameters: BuildParameters.Testing) -> [String: String] { var settings: [String: String] = [:] - // TODO: enableCodeCoverage - // explicitlyEnabledTestability + + settings["CLANG_COVERAGE_MAPPING"] = parameters.enableCodeCoverage ? "YES" : "NO" switch parameters.explicitlyEnabledTestability { case true: diff --git a/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift b/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift index 99de658066a..3978f92b874 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift @@ -54,6 +54,13 @@ extension Trait where Self == Testing.Bug { ) } + public static var IssueWindowsPathTestsFailures: Self { + .issue( + "https://github.com/swiftlang/swift-package-manager/issues/8511", + relationship: .defect, + ) + } + public static var IssueWindowsCannotSaveAttachment: Self { // error: unable to write file 'C:\Users\ContainerAdministrator\AppData\Local\Temp\CFamilyTargets_CDynamicLookup.hNxGHC\CFamilyTargets_CDynamicLookup\.build\x86_64-unknown-windows-msvc\Intermediates.noindex\CDynamicLookup.build\Release-windows\CDynamicLookup.build\Objects-normal\x86_64\CDynamicLookup.LinkFileList': No such file or directory (2) .issue( diff --git a/Tests/BasicsTests/FileSystem/PathTests.swift b/Tests/BasicsTests/FileSystem/PathTests.swift index 5cf741f1a34..0f6039a2524 100644 --- a/Tests/BasicsTests/FileSystem/PathTests.swift +++ b/Tests/BasicsTests/FileSystem/PathTests.swift @@ -43,6 +43,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "/ab/cd/ef/", expected: (windows ? #"\ab\cd\ef"# : "/ab/cd/ef"), label: "Trailing path seperator"), (path: "/ab/cd/ef//", expected: (windows ? #"\ab\cd\ef"# : "/ab/cd/ef"), label: "Trailing path seperator"), @@ -110,6 +111,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "/./a", expected: (windows ? #"\"# : "/")), (path: "/../..", expected: (windows ? #"\"# : "/")), @@ -143,6 +145,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "/../..", expected: "/"), ] @@ -178,6 +181,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "/../..", expected: "/"), ] @@ -204,6 +208,7 @@ struct PathTests { #expect(actual == expectedPath) } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "/", numParentDirectoryCalls: 1, expected: "/"), (path: "/", numParentDirectoryCalls: 2, expected: "/"), @@ -215,6 +220,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "/bar/../foo/..//", numParentDirectoryCalls: 2, expected: "/"), (path: "/bar/../foo/..//yabba/a/b", numParentDirectoryCalls: 2, expected: "/yabba") @@ -231,6 +237,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "/", expected: ["/"]), (path: "/.", expected: ["/"]), @@ -329,6 +336,87 @@ struct PathTests { } } + struct absolutePathValidationWithPathContainsLeadingAndTrailingWhitespacesReturnsExpectedValue { + func testImplementation( + data: (String, String), + whitespaces: String, + ) throws { + let path = data.0 + let expected = data.1 + do { + // Leading whitespaces + let actual = try AbsolutePath(validating: "\(whitespaces)\(path)").pathString + #expect(actual == expected, "Actual is not as expected. Path is: '\(path)'") + } + + do { + // Training whitespaces + let actual = try AbsolutePath(validating: "\(path)\(whitespaces)").pathString + #expect(actual == expected, "Actual is not as expected. Path is: '\(path)'") + } + + do { + // Leading and trailing whitespaces + let actual = try AbsolutePath(validating: "\(whitespaces)\(path)\(whitespaces)").pathString + #expect(actual == expected, "Actual is not as expected. Path is: '\(path)'") + } + } + @Test( + arguments: [ + (path: "/", expected: (windows ? #"\"# : "/")), + ], [ + " ", + " ", + "\t", + "\t\t", + "\n", + "\n\n", + "\t ", + " \t", + " \n\t", + "\n \t", + ], + ) + func absolutePathValidationWithPathContainsLeadingAndTrailingWhitespaces( + data: (String, String), + whitespaces: String, + ) throws { + try testImplementation(data: data, whitespaces: whitespaces) + } + + @Test( + .IssueWindowsPathTestsFailures, + arguments: [ + (path: "/.", expected: (windows ? #"\"# : "/")), + (path: "/..", expected: (windows ? #"\"# : "/")), + (path: "/bar/", expected: (windows ? #"\bar"# : "/bar")), + ], [ + " ", + " ", + "\t", + "\t\t", + "\n", + "\n\n", + "\t ", + " \t", + " \n\t", + "\n \t", + ], + ) + func absolutePathValidationWithPathContainsLeadingAndTrailingWhitespacesFailsOnWindows( + data: (String, String), + whitespaces: String, + ) throws { + try withKnownIssue(": Path \(data.0) is not handled properly") { + try testImplementation(data: data, whitespaces: whitespaces) + } when: { + ProcessInfo.hostOperatingSystem == .windows + } + } + + } + + @Test func comparison() { #expect(AbsolutePath("/") <= AbsolutePath("/")); @@ -368,6 +456,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "ab//cd//ef", expected: (windows ? #"ab\cd\ef"# : "ab/cd/ef"), label: "repeated path seperators"), (path: "ab//cd///ef", expected: (windows ? #"ab\cd\ef"# : "ab/cd/ef"), label: "repeated path seperators"), @@ -440,7 +529,8 @@ struct PathTests { } @Test( - arguments: [ + .IssueWindowsPathTestsFailures, + arguments: [ (path: "../a/..", expected: "."), (path: "a/..", expected: "."), (path: "a/../////../////./////", expected: "."), @@ -481,6 +571,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "a/..", expected: "."), (path: "a/../////../////./////", expected: ".."), @@ -519,6 +610,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "../..", expected: ".."), (path: "../a/..", expected: ".."), @@ -560,7 +652,8 @@ struct PathTests { } @Test( - arguments:[ + .IssueWindowsPathTestsFailures, + arguments:[ "a.", ".a", "", @@ -601,6 +694,7 @@ struct PathTests { } @Test( + .IssueWindowsPathTestsFailures, arguments: [ (path: "foo/bar/..", expected: ["foo"]), (path: "bar/../foo", expected: ["foo"]), @@ -622,13 +716,15 @@ struct PathTests { } } - @Test + @Test( + .IssueWindowsPathTestsFailures, + ) func relativePathValidation() throws { #expect(throws: Never.self) { try RelativePath(validating: "a/b/c/d") } - withKnownIssue { + withKnownIssue("https://github.com/swiftlang/swift-package-manager/issues/8511: \\") { #expect {try RelativePath(validating: "/a/b/d")} throws: { error in ("\(error)" == "invalid relative path '/a/b/d'; relative path should not begin with '/'") } @@ -637,6 +733,87 @@ struct PathTests { } } + struct relativePathValidationWithPathContainsLeadingAndTrailingWhitespacesReturnsExpectedValue { + func testImplementation( + data: (String, String), + whitespaces: String, + ) async throws { + let path = data.0 + let expected = data.1 + do { + // Leading whitespaces + let actual = try RelativePath(validating: "\(whitespaces)\(path)").pathString + #expect(actual == expected, "Actual is not as expected. Path is: '\(path)'") + } + + do { + // Training whitespaces + let actual = try RelativePath(validating: "\(path)\(whitespaces)").pathString + #expect(actual == expected, "Actual is not as expected. Path is: '\(path)'") + } + + do { + // Leading and trailing whitespaces + let actual = try RelativePath(validating: "\(whitespaces)\(path)\(whitespaces)").pathString + #expect(actual == expected, "Actual is not as expected. Path is: '\(path)'") + } + } + + @Test( + arguments: [ + (path: ".", expected: "."), + (path: "bar/", expected: (windows ? #"bar\"# : "bar")), + (path: "bar/baz", expected: (windows ? #"bar\baz"# :"bar/baz")), + ], [ + " ", + " ", + "\t", + "\t\t", + "\n", + "\n\n", + "\t ", + " \t", + " \n\t", + "\n \t", + ], + ) + func relativePathValidationWithPathContainsLeadingAndTrailingWhitespaces( + data: (String, String), + whitespaces: String, + ) async throws { + try await testImplementation(data: data, whitespaces: whitespaces) + } + + + @Test( + .IssueWindowsPathTestsFailures, + arguments: [ + (path: "bar/", expected: "bar"), + ] as [(String, String)], [ + " ", + " ", + "\t", + "\t\t", + "\n", + "\n\n", + "\t ", + " \t", + " \n\t", + "\n \t", + ], + ) + func relativePathValidationWithPathContainsLeadingAndTrailingWhitespacesFailsOnWindows( + data: (String, String), + whitespaces: String, + ) async throws { + try await withKnownIssue("https://github.com/swiftlang/swift-package-manager/issues/8511: Path \(data.0) is not properly") { + try await testImplementation(data: data, whitespaces: whitespaces) + } when: { + ProcessInfo.hostOperatingSystem == .windows + } + } + } + } @Test diff --git a/Tests/BasicsTests/HTTPClientTests.swift b/Tests/BasicsTests/HTTPClientTests.swift index 6ea4442b3db..b4402c55919 100644 --- a/Tests/BasicsTests/HTTPClientTests.swift +++ b/Tests/BasicsTests/HTTPClientTests.swift @@ -259,11 +259,8 @@ struct HTTPClientTests { var request = HTTPClient.Request(method: .get, url: "http://test") request.options.validResponseCodes = [200] - do { - let response = try await httpClient.execute(request) - Issue.record("unexpected success \(response)") - } catch { - #expect(error as? HTTPClientError == .badResponseStatusCode(statusCode)) + await #expect(throws: HTTPClientError.badResponseStatusCode(statusCode)) { + try await httpClient.execute(request) } } @@ -407,11 +404,8 @@ struct HTTPClientTests { var request = HTTPClient.Request(url: "http://test") request.options.maximumResponseSizeInBytes = 10 - do { - let response = try await httpClient.execute(request) - Issue.record("unexpected success \(response)") - } catch { - #expect(error as? HTTPClientError == .responseTooLarge(maxSize * 2)) + await #expect(throws: HTTPClientError.responseTooLarge(maxSize * 2)) { + try await httpClient.execute(request) } } diff --git a/Tests/BuildTests/PluginsBuildPlanTests.swift b/Tests/BuildTests/PluginsBuildPlanTests.swift index 86158adfc16..60e04740d1e 100644 --- a/Tests/BuildTests/PluginsBuildPlanTests.swift +++ b/Tests/BuildTests/PluginsBuildPlanTests.swift @@ -28,7 +28,7 @@ struct PluginsBuildPlanTests { .tags( .Feature.Command.Build, ), - .issue("https://github.com/swiftlang/swift-package-manager/issues/8511", relationship: .defect), // Fails to build the project to due to incorrect Path handling + .IssueWindowsPathTestsFailures, // Fails to build the project to due to incorrect Path handling arguments: BuildConfiguration.allCases, ) func buildToolsDatabasePath( diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index aaa83258257..1ee9ed8c50d 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -32,141 +32,130 @@ struct BuildResult { let moduleContents: [String] } -@Suite( - .tags( - Tag.TestSize.small, - ), -) -struct SanitierTests { - @Test( - arguments: Sanitizer.allCases +@discardableResult +fileprivate func execute( + _ args: [String] = [], + environment: Environment? = nil, + packagePath: AbsolutePath? = nil, + configuration: BuildConfiguration, + buildSystem: BuildSystemProvider.Kind, + throwIfCommandFails: Bool = true, +) async throws -> (stdout: String, stderr: String) { + + return try await executeSwiftBuild( + packagePath, + configuration: configuration, + extraArgs: args, + env: environment, + buildSystem: buildSystem, + throwIfCommandFails: throwIfCommandFails, ) - func creatingSanitizers(sanitizer: Sanitizer) throws { - #expect(sanitizer == Sanitizer(argument: sanitizer.shortName)) - } - - @Test - func invalidSanitizer() throws { - #expect(Sanitizer(argument: "invalid") == nil) - } } -@Suite( - .tags( - Tag.TestSize.large, - Tag.Feature.Command.Build, - ), -) -struct BuildCommandTestCases { - - @discardableResult - func execute( - _ args: [String] = [], - environment: Environment? = nil, - packagePath: AbsolutePath? = nil, - configuration: BuildConfiguration = .debug, - buildSystem: BuildSystemProvider.Kind, - throwIfCommandFails: Bool = true, - ) async throws -> (stdout: String, stderr: String) { - - return try await executeSwiftBuild( - packagePath, +fileprivate func build( + _ args: [String], + packagePath: AbsolutePath? = nil, + configuration: BuildConfiguration, + cleanAfterward: Bool = true, + buildSystem: BuildSystemProvider.Kind, +) async throws -> BuildResult { + do { + let (stdout, stderr) = try await execute(args, packagePath: packagePath,configuration: configuration, buildSystem: buildSystem,) + defer { + } + let (binPathOutput, _) = try await execute( + ["--show-bin-path"], + packagePath: packagePath, configuration: configuration, - extraArgs: args, - env: environment, buildSystem: buildSystem, - throwIfCommandFails: throwIfCommandFails, ) - } - - func build( - _ args: [String], - packagePath: AbsolutePath? = nil, - configuration: BuildConfiguration = .debug, - cleanAfterward: Bool = true, - buildSystem: BuildSystemProvider.Kind, - ) async throws -> BuildResult { - do { - // let buildConfigurationArguments = isRelease ? ["-c", "release"] : [] - let (stdout, stderr) = try await execute(args, packagePath: packagePath,configuration: configuration, buildSystem: buildSystem,) - defer { + let binPath = try AbsolutePath(validating: binPathOutput.trimmingCharacters(in: .whitespacesAndNewlines)) + let binContents = try localFileSystem.getDirectoryContents(binPath).filter { + guard let contents = try? localFileSystem.getDirectoryContents(binPath.appending(component: $0)) else { + return true } - let (binPathOutput, _) = try await execute( - ["--show-bin-path"], - packagePath: packagePath, - configuration: configuration, - buildSystem: buildSystem, - ) - let binPath = try AbsolutePath(validating: binPathOutput.trimmingCharacters(in: .whitespacesAndNewlines)) - let binContents = try localFileSystem.getDirectoryContents(binPath).filter { - guard let contents = try? localFileSystem.getDirectoryContents(binPath.appending(component: $0)) else { - return true - } - // Filter directories which only contain an output file map since we didn't build anything for those which - // is what `binContents` is meant to represent. - return contents != ["output-file-map.json"] - } - var moduleContents: [String] = [] - if buildSystem == .native { - moduleContents = (try? localFileSystem.getDirectoryContents(binPath.appending(component: "Modules"))) ?? [] - } else { - let moduleDirs = (try? localFileSystem.getDirectoryContents(binPath).filter { - $0.hasSuffix(".swiftmodule") - }) ?? [] - for dir: String in moduleDirs { - moduleContents += - (try? localFileSystem.getDirectoryContents(binPath.appending(component: dir)).map { "\(dir)/\($0)" }) ?? [] - } + // Filter directories which only contain an output file map since we didn't build anything for those which + // is what `binContents` is meant to represent. + return contents != ["output-file-map.json"] + } + var moduleContents: [String] = [] + if buildSystem == .native { + moduleContents = (try? localFileSystem.getDirectoryContents(binPath.appending(component: "Modules"))) ?? [] + } else { + let moduleDirs = (try? localFileSystem.getDirectoryContents(binPath).filter { + $0.hasSuffix(".swiftmodule") + }) ?? [] + for dir: String in moduleDirs { + moduleContents += + (try? localFileSystem.getDirectoryContents(binPath.appending(component: dir)).map { "\(dir)/\($0)" }) ?? [] } + } - if cleanAfterward { - try await executeSwiftPackage( - packagePath, - extraArgs: ["clean"], - buildSystem: buildSystem - ) - } - return BuildResult( - binPath: binPath, - stdout: stdout, - stderr: stderr, - binContents: binContents, - moduleContents: moduleContents + if cleanAfterward { + try await executeSwiftPackage( + packagePath, + extraArgs: ["clean"], + buildSystem: buildSystem + ) + } + return BuildResult( + binPath: binPath, + stdout: stdout, + stderr: stderr, + binContents: binContents, + moduleContents: moduleContents + ) + } catch { + if cleanAfterward { + try await executeSwiftPackage( + packagePath, + extraArgs: ["clean"], + buildSystem: buildSystem ) - } catch { - if cleanAfterward { - try await executeSwiftPackage( - packagePath, - extraArgs: ["clean"], - buildSystem: buildSystem - ) - } - throw error } + throw error } +} + +@Suite( + .tags( + Tag.TestSize.large, + Tag.Feature.Command.Build, + ), +) +struct BuildCommandTestCases { + @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) - func usage(buildSystem: BuildSystemProvider.Kind) async throws { - let stdout = try await execute(["-help"], buildSystem: buildSystem).stdout + func usage( + data: BuildData, + ) async throws { + let stdout = try await execute(["-help"], configuration: data.config, buildSystem: data.buildSystem).stdout #expect(stdout.contains("USAGE: swift build")) } @Test( - arguments: SupportedBuildSystemOnPlatform, BuildConfiguration.allCases + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func binSymlink( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration, + buildData: BuildData, ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config // Test is not implemented for Xcode build system try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) let targetPath = try fullPath.appending(components: buildSystem.binPath(for: configuration)) - let path = try await self.execute(["--show-bin-path"], packagePath: fullPath, configuration: configuration, buildSystem: buildSystem).stdout.trimmingCharacters(in: .whitespacesAndNewlines) + let path = try await execute( + ["--show-bin-path"], + packagePath: fullPath, + configuration: configuration, + buildSystem: buildSystem, + ).stdout.trimmingCharacters(in: .whitespacesAndNewlines) #expect( AbsolutePath(path).pathString == targetPath.pathString ) @@ -174,44 +163,67 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) - func seeAlso(buildSystem: BuildSystemProvider.Kind) async throws { - let stdout = try await execute(["--help"], buildSystem: buildSystem).stdout + func seeAlso( + data: BuildData, + ) async throws { + let stdout = try await execute( + ["--help"], + configuration: data.config, + buildSystem: data.buildSystem, + ).stdout #expect(stdout.contains("SEE ALSO: swift run, swift package, swift test")) } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) - func commandDoesNotEmitDuplicateSymbols(buildSystem: BuildSystemProvider.Kind) async throws { + func commandDoesNotEmitDuplicateSymbols( + data: BuildData, + ) async throws { let duplicateSymbolRegex = try #require(duplicateSymbolRegex) - let (stdout, stderr) = try await execute(["--help"], buildSystem: buildSystem) + let (stdout, stderr) = try await execute( + ["--help"], + configuration: data.config, + buildSystem: data.buildSystem, + ) #expect(!stdout.contains(duplicateSymbolRegex)) #expect(!stderr.contains(duplicateSymbolRegex)) } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) - func version(buildSystem: BuildSystemProvider.Kind) async throws { - let stdout = try await execute(["--version"], buildSystem: buildSystem).stdout + func version( + data: BuildData, + ) async throws { + let stdout = try await execute( + ["--version"], + configuration: data.config, + buildSystem: data.buildSystem, + ).stdout let expectedRegex = try Regex(#"Swift Package Manager -( \w+ )?\d+.\d+.\d+(-\w+)?"#) #expect(stdout.contains(expectedRegex)) } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) - func importOfMissedDepWarning(buildSystem: BuildSystemProvider.Kind) async throws { + func importOfMissedDepWarning( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config try await withKnownIssue("SWBINTTODO: Test fails because the warning message regarding missing imports is expected to be more verbose and actionable at the SwiftPM level with mention of the involved targets. This needs to be investigated. See case targetDiagnostic(TargetDiagnosticInfo) as a message type that may help.") { try await fixture(name: "Miscellaneous/ImportOfMissingDependency") { path in let fullPath = try resolveSymlinks(path) let error = await #expect(throws: SwiftPMError.self ) { - try await self.build( + try await build( ["--explicit-target-dependency-import-check=warn"], packagePath: fullPath, + configuration: configuration, buildSystem: buildSystem, ) } @@ -231,16 +243,21 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) - func importOfMissedDepWarningVerifyingErrorFlow(buildSystem: BuildSystemProvider.Kind) async throws { + func importOfMissedDepWarningVerifyingErrorFlow( + data: BuildData + ) async throws { + let buildSystem = data.buildSystem + let config = data.config try await withKnownIssue("SWBINTTODO: Test fails because the warning message regarding missing imports is expected to be more verbose and actionable at the SwiftPM level with mention of the involved targets. This needs to be investigated. See case targetDiagnostic(TargetDiagnosticInfo) as a message type that may help.") { try await fixture(name: "Miscellaneous/ImportOfMissingDependency") { path in let fullPath = try resolveSymlinks(path) let error = await #expect(throws: SwiftPMError.self ) { - try await self.build( + try await build( ["--explicit-target-dependency-import-check=error"], packagePath: fullPath, + configuration: config, buildSystem: buildSystem, ) } @@ -260,13 +277,20 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) - func importOfMissedDepWarningVerifyingDefaultDoesNotRunTheCheck(buildSystem: BuildSystemProvider.Kind) async throws { + func importOfMissedDepWarningVerifyingDefaultDoesNotRunTheCheck( + data: BuildData, + ) async throws { try await fixture(name: "Miscellaneous/ImportOfMissingDependency") { path in let fullPath = try resolveSymlinks(path) let error = await #expect(throws: SwiftPMError.self ) { - try await self.build([], packagePath: fullPath, buildSystem: buildSystem) + try await build( + [], + packagePath: fullPath, + configuration: data.config, + buildSystem: data.buildSystem, + ) } guard case SwiftPMError.executionFailure(_, _, let stderr) = try #require(error) else { Issue.record("Expected error did not occur") @@ -291,7 +315,7 @@ struct BuildCommandTestCases { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) // Test symlink. - try await self.execute(packagePath: fullPath, configuration: configuration, buildSystem: buildSystem) + try await execute(packagePath: fullPath, configuration: configuration, buildSystem: buildSystem) let actualDebug = try resolveSymlinks(fullPath.appending(components: buildSystem.binPath(for: configuration))) let expectedDebug = try fullPath.appending(components: buildSystem.binPath(for: configuration)) #expect(actualDebug == expectedDebug) @@ -302,61 +326,88 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + .IssueWindowsLongPath, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildExistingExecutableProductIsSuccessfull( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { - try await withKnownIssue("Failures possibly due to long file paths") { + try await withKnownIssue("Failures possibly due to long file paths", isIntermittent: true) { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) - let result = try await build(["--product", "exec1"], packagePath: fullPath, buildSystem: buildSystem,) + let result = try await build( + ["--product", "exec1"], + packagePath: fullPath, + configuration: data.config, + buildSystem: data.buildSystem, + ) #expect(result.binContents.contains(executableName("exec1"))) #expect(!result.binContents.contains("exec2.build")) } } when: { - ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && data.buildSystem == .swiftbuild } } @Test( - .SWBINTTODO("Found multiple targets named 'lib1'"), - arguments: SupportedBuildSystemOnPlatform, + .issue("https://github.com/swiftlang/swift-package-manager/issues/9137", relationship: .defect), + .IssueWindowsCannotSaveAttachment, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildExistingLibraryProductIsSuccessfull( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { - try await withKnownIssue("Found multiple targets named 'lib1'") { + let buildSystem = data.buildSystem + try await withKnownIssue(isIntermittent: true) { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) - let (_, stderr) = try await execute(["--product", "lib1"], packagePath: fullPath, buildSystem: buildSystem,) - if buildSystem != .xcode { - #expect( - stderr.contains( - "'--product' cannot be used with the automatic product 'lib1'; building the default target instead" - ) - ) + let (_, stderr) = try await execute( + ["--product", "lib1"], + packagePath: fullPath, + configuration: data.config, + buildSystem: buildSystem, + ) + switch buildSystem { + case .native, .swiftbuild: + withKnownIssue("Found multiple targets named 'lib1'") { + #expect( + stderr.contains( + "'--product' cannot be used with the automatic product 'lib1'; building the default target instead" + ) + ) + } when: { + .swiftbuild == buildSystem + } + case .xcode: + // Do nothing. + break } } } when: { - .swiftbuild == buildSystem + ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild } } @Test( - .SWBINTTODO("Could not find target named 'exec2'"), - arguments: SupportedBuildSystemOnPlatform, + .issue("https://github.com/swiftlang/swift-package-manager/issues/9138", relationship: .defect), + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildExistingTargetIsSuccessfull( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem try await withKnownIssue("Could not find target named 'exec2'") { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) - let result = try await build(["--target", "exec2"], packagePath: fullPath, buildSystem: buildSystem,) + let result = try await build( + ["--target", "exec2"], + packagePath: fullPath, + configuration: data.config, + buildSystem: buildSystem, + ) #expect(result.binContents.contains("exec2.build")) #expect(!result.binContents.contains(executableName("exec1"))) } @@ -369,16 +420,18 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildProductAndTargetsFailsWithAMutuallyExclusiveMessage( - buildSystem: BuildSystemProvider.Kind, + buildData: BuildData, ) async throws { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( - ["--product", "exec1", "--target", "exec2"], - packagePath: fixturePath,buildSystem: buildSystem, + try await execute( + ["--product", "exec1", "--target", "exec2"], + packagePath: fixturePath, + configuration: buildData.config, + buildSystem: buildData.buildSystem, ) } // THEN I expect a failure @@ -391,16 +444,18 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildProductAndTestsFailsWithAMutuallyExclusiveMessage( - buildSystem: BuildSystemProvider.Kind, + buildData: BuildData, ) async throws { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( - ["--product", "exec1", "--build-tests"], - packagePath: fixturePath,buildSystem: buildSystem, + try await execute( + ["--product", "exec1", "--build-tests"], + packagePath: fixturePath, + configuration: buildData.config, + buildSystem: buildData.buildSystem, ) } // THEN I expect a failure @@ -413,16 +468,18 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildTargetAndTestsFailsWithAMutuallyExclusiveMessage( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( - ["--build-tests", "--target", "exec2"], - packagePath: fixturePath,buildSystem: buildSystem, + try await execute( + ["--build-tests", "--target", "exec2"], + packagePath: fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, ) } // THEN I expect a failure @@ -435,16 +492,18 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildProductTargetAndTestsFailsWithAMutuallyExclusiveMessage( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( - ["--build-tests", "--target", "exec2", "--product", "exec1"], - packagePath: fixturePath,buildSystem: buildSystem, + try await execute( + ["--build-tests", "--target", "exec2", "--product", "exec1"], + packagePath: fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, ) } // THEN I expect a failure @@ -452,22 +511,32 @@ struct BuildCommandTestCases { Issue.record("Incorrect error was raised.") return } - #expect(stderr.contains("error: '--product', '--target', and '--build-tests' are mutually exclusive")) + withKnownIssue(isIntermittent: true) { + #expect(stderr.contains("error: '--product', '--target', and '--build-tests' are mutually exclusive"), "stout: \(stdout)") + } when: { + ( + ProcessInfo.hostOperatingSystem == .windows && ( + data.buildSystem == .native + || (data.buildSystem == .swiftbuild && data.config == .debug) + )) + } } } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildUnknownProductFailsWithAppropriateMessage( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in let productName = "UnknownProduct" let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( - ["--product", productName], - packagePath: fixturePath,buildSystem: buildSystem, + try await execute( + ["--product", productName], + packagePath: fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, ) } // THEN I expect a failure @@ -476,30 +545,34 @@ struct BuildCommandTestCases { return } - if .native == buildSystem { - #expect(stderr.contains("error: no product named '\(productName)'")) - } else { - let expectedErrorMessageRegex = try Regex("error: Could not find target named '\(productName).*'") - #expect( - stderr.contains(expectedErrorMessageRegex), - "expect log not emitted.\nstdout: '\(stdout)'\n\nstderr: '\(stderr)'", - ) + switch data.buildSystem { + case .native: + #expect(stderr.contains("error: no product named '\(productName)'")) + case .swiftbuild, .xcode: + let expectedErrorMessageRegex = try Regex("error: Could not find target named '\(productName).*'") + #expect( + stderr.contains(expectedErrorMessageRegex), + "expect log not emitted.\nstdout: '\(stdout)'\n\nstderr: '\(stderr)'", + ) } } } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildUnknownTargetFailsWithAppropriateMessage( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in + let buildSystem = data.buildSystem let targetName = "UnknownTargetName" let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( - ["--target", targetName], - packagePath: fixturePath,buildSystem: buildSystem, + try await execute( + ["--target", targetName], + packagePath: fixturePath, + configuration: data.config, + buildSystem: buildSystem, ) } // THEN I expect a failure @@ -508,10 +581,11 @@ struct BuildCommandTestCases { return } let expectedErrorMessage: String - if .native == buildSystem { - expectedErrorMessage = "error: no target named '\(targetName)'" - } else { - expectedErrorMessage = "error: Could not find target named '\(targetName)'" + switch buildSystem { + case .native: + expectedErrorMessage = "error: no target named '\(targetName)'" + case .swiftbuild, .xcode: + expectedErrorMessage = "error: Could not find target named '\(targetName)'" } #expect( stderr.contains(expectedErrorMessage), @@ -521,43 +595,43 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ["ClangExecSingleFile", "SwiftExecSingleFile", "SwiftExecMultiFile"], ) - func atMainSupport(buildSystem: BuildSystemProvider.Kind) async throws { - try await withKnownIssue("SWBINTTODO: File not found or missing libclang errors on non-macOS platforms. This needs to be investigated") { + func atMainSupport( + data: BuildData, + executable: String, + ) async throws { + let buildSystem = data.buildSystem + let config = data.config + try await withKnownIssue( + "SWBINTTODO: File not found or missing libclang errors on non-macOS platforms. This needs to be investigated", + isIntermittent: true, + ) { try await fixture(name: "Miscellaneous/AtMainSupport") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) - - do { - let result = try await build(["--product", "ClangExecSingleFile"], packagePath: fullPath, buildSystem: buildSystem) - #expect(result.binContents.contains(executableName("ClangExecSingleFile"))) - } - - do { - let result = try await build(["--product", "SwiftExecSingleFile"], packagePath: fullPath, buildSystem: buildSystem) - #expect(result.binContents.contains(executableName("SwiftExecSingleFile"))) - } - - do { - let result = try await build(["--product", "SwiftExecMultiFile"], packagePath: fullPath, buildSystem: buildSystem) - #expect(result.binContents.contains(executableName("SwiftExecMultiFile"))) - } + let result = try await build(["--product", executable], packagePath: fullPath, configuration: config, buildSystem: buildSystem) + #expect(result.binContents.contains(executableName(executable))) } } when: { - ![.macOS, .linux].contains(ProcessInfo.hostOperatingSystem) && buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild } } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func nonReachableProductsAndTargetsFunctional( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { try await fixture(name: "Miscellaneous/UnreachableTargets") { fixturePath in let aPath = fixturePath.appending("A") - let result = try await build([], packagePath: aPath, buildSystem: buildSystem) + let result = try await build( + [], + packagePath: aPath, + configuration: data.config, + buildSystem: data.buildSystem, + ) #expect(!result.binContents.contains("bexec")) #expect(!result.binContents.contains("BTarget2.build")) #expect(!result.binContents.contains("cexec")) @@ -566,20 +640,24 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) func nonReachableProductsAndTargetsFunctionalWhereDependencyContainsADependentProducts( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { - // skipped on Xcode - // known failures in SwiftBuild + let buildSystem = data.buildSystem try await withKnownIssue("SWBINTTODO: Test failed. This needs to be investigated") { try await fixture(name: "Miscellaneous/UnreachableTargets") { fixturePath in let aPath = fixturePath.appending("A") // Dependency contains a dependent product - let result = try await build(["--product", "bexec"], packagePath: aPath, buildSystem: buildSystem) + let result = try await build( + ["--product", "bexec"], + packagePath: aPath, + configuration: data.config, + buildSystem: buildSystem, + ) #expect(result.binContents.contains("BTarget2.build")) #expect(result.binContents.contains(executableName("bexec"))) #expect(!result.binContents.contains(executableName("aexec"))) @@ -603,28 +681,35 @@ struct BuildCommandTestCases { #expect(!result.moduleContents.contains("CTarget.swiftinterface")) } } when: { - buildSystem != .native + buildSystem == .swiftbuild } } @Test( - arguments: SupportedBuildSystemOnPlatform, + .issue("https://github.com/swiftlang/swift-package-manager/pull/9130", relationship: .fixedBy), + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func parseableInterfaces( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem try await fixture(name: "Miscellaneous/ParseableInterfaces") { fixturePath in - try await withKnownIssue { - let result = try await build(["--enable-parseable-module-interfaces"], packagePath: fixturePath, buildSystem: buildSystem) + try await withKnownIssue(isIntermittent: ProcessInfo.hostOperatingSystem == .windows) { + let result = try await build( + ["--enable-parseable-module-interfaces"], + packagePath: fixturePath, + configuration: data.config, + buildSystem: buildSystem, + ) switch buildSystem { case .native: #expect(result.moduleContents.contains("A.swiftinterface")) #expect(result.moduleContents.contains("B.swiftinterface")) - default: - let moduleARegex = try Regex(#"A[.]swiftmodule[/].*[.]swiftinterface"#) - let moduleBRegex = try Regex(#"B[.]swiftmodule[/].*[.]swiftmodule"#) - #expect(result.moduleContents.contains { $0.contains(moduleARegex) }) - #expect(result.moduleContents.contains { $0.contains(moduleBRegex) }) + case .swiftbuild, .xcode: + let moduleARegex = try Regex(#"A[.]swiftmodule[/].*[.]swiftinterface"#) + let moduleBRegex = try Regex(#"B[.]swiftmodule[/].*[.]swiftmodule"#) + #expect(result.moduleContents.contains { $0.contains(moduleARegex) }) + #expect(result.moduleContents.contains { $0.contains(moduleBRegex) }) } } when: { // errors with SwiftBuild on Windows possibly due to long path on windows only for swift build @@ -634,19 +719,25 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func automaticParseableInterfacesWithLibraryEvolution( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem try await withKnownIssue { try await fixture(name: "Miscellaneous/LibraryEvolution") { fixturePath in - let result = try await build([], packagePath: fixturePath, buildSystem: buildSystem) + let result = try await build( + [], + packagePath: fixturePath, + configuration: data.config, + buildSystem: buildSystem, + ) switch buildSystem { case .native: #expect(result.moduleContents.contains("A.swiftinterface")) #expect(result.moduleContents.contains("B.swiftinterface")) - default: + case .swiftbuild, .xcode: let moduleARegex = try Regex(#"A[.]swiftmodule[/].*[.]swiftinterface"#) let moduleBRegex = try Regex(#"B[.]swiftmodule[/].*[.]swiftmodule"#) withKnownIssue("SWBINTTODO: Test failed because of missing 'A.swiftmodule/*.swiftinterface' files") { @@ -663,16 +754,21 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildCompleteMessage( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem try await withKnownIssue { try await fixture(name: "DependencyResolution/Internal/Simple") { fixturePath in let buildCompleteRegex = try Regex(#"Build complete!\s?(\([0-9]*\.[0-9]*\s*s(econds)?\))?"#) do { - let result = try await execute(packagePath: fixturePath, buildSystem: buildSystem) + let result = try await execute( + packagePath: fixturePath, + configuration: data.config, + buildSystem: buildSystem, + ) // This test fails to match the 'Compiling' regex; rdar://101815761 // XCTAssertMatch(result.stdout, .regex("\\[[1-9][0-9]*\\/[1-9][0-9]*\\] Compiling")) let lines = result.stdout.split(whereSeparator: { $0.isNewline }) @@ -682,12 +778,20 @@ struct BuildCommandTestCases { do { // test second time, to stabilize the cache - try await self.execute(packagePath: fixturePath, buildSystem: buildSystem) + try await execute( + packagePath: fixturePath, + configuration: data.config, + buildSystem: buildSystem, + ) } do { // test third time, to make sure message is presented even when nothing to build (cached) - let result = try await execute(packagePath: fixturePath, buildSystem: buildSystem) + let result = try await execute( + packagePath: fixturePath, + configuration: data.config, + buildSystem: buildSystem, + ) // This test fails to match the 'Compiling' regex; rdar://101815761 // XCTAssertNoMatch(result.stdout, .regex("\\[[1-9][0-9]*\\/[1-9][0-9]*\\] Compiling")) let lines = result.stdout.split(whereSeparator: { $0.isNewline }) @@ -696,17 +800,18 @@ struct BuildCommandTestCases { } } } when: { - buildSystem == .swiftbuild && ((ProcessInfo.hostOperatingSystem == .windows)) + buildSystem == .swiftbuild && (ProcessInfo.hostOperatingSystem == .windows) } } @Test( - arguments: SupportedBuildSystemOnPlatform, BuildConfiguration.allCases, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildStartMessage( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem + let configuration = data.config try await fixture(name: "DependencyResolution/Internal/Simple") { fixturePath in let result = try await execute([], packagePath: fixturePath, configuration: configuration, buildSystem: buildSystem, throwIfCommandFails: false) let expectedString: String @@ -717,22 +822,27 @@ struct BuildCommandTestCases { expectedString = "production" } - withKnownIssue("Xcode build system does not emit the build started message.") { - #expect( - result.stdout.contains("Building for \(expectedString)"), - "expect log not emitted. got stdout: '\(result.stdout)'\n\nstderr '\(result.stderr)'") - } when: { - buildSystem == .xcode + switch buildSystem { + case .native, .swiftbuild: + #expect( + result.stdout.contains("Building for \(expectedString)"), + "expect log not emitted. got stdout: '\(result.stdout)'\n\nstderr '\(result.stderr)'", + ) + case .xcode: + // Xcode build system does not emit the build started message. + break } } } @Test( - arguments: SupportedBuildSystemOnPlatform, + .IssueWindowsLongPath, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func buildSystemDefaultSettings( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem try await withKnownIssue("Sometimes failed to build due to a possible path issue", isIntermittent: true) { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in // try await building using XCBuild with default parameters. This should succeed. We build verbosely so we get @@ -740,7 +850,7 @@ struct BuildCommandTestCases { let output: (stdout: String, stderr: String) = try await execute( ["-v"], packagePath: fixturePath, - configuration: .debug, + configuration: data.config, buildSystem: buildSystem, ) @@ -756,18 +866,20 @@ struct BuildCommandTestCases { #expect(output.stdout.contains("Build complete!")) } } when: { - buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .windows + (buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .windows) + || (buildSystem == .native && data.config == .release && ProcessInfo.hostOperatingSystem == .windows) } } @Test( .disabled("Disabled for now because it is hitting 'IR generation failure: Cannot read legacy layout file' in CI (rdar://88828632)"), - arguments: [BuildSystemProvider.Kind.swiftbuild, .xcode], BuildConfiguration.allCases + arguments: getBuildData(for: [BuildSystemProvider.Kind.swiftbuild, .xcode]), ) func xcodeBuildSystemWithAdditionalBuildFlags( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration + data: BuildData ) async throws { + let configuration = data.config + let buildSystem = data.buildSystem try await fixture(name: "ValidLayouts/SingleModule/ExecutableMixed") { fixturePath in // try await building using XCBuild with additional flags. This should succeed. We build verbosely so we get // full command lines. @@ -794,20 +906,22 @@ struct BuildCommandTestCases { @Test( .requireHostOS(.macOS), - arguments: [BuildSystemProvider.Kind.xcode], + arguments: getBuildData(for: [BuildSystemProvider.Kind.swiftbuild, .xcode]), ) func buildSystemOverrides( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { + let buildSystem = data.buildSystem + let config = data.config try await withKnownIssue { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in let swiftCompilerPath = try UserToolchain.default.swiftCompilerPath // try await building without specifying overrides. This should succeed, and should use the default // compiler path. - let defaultOutput = try await self.execute( + let defaultOutput = try await execute( ["--vv"], packagePath: fixturePath, - configuration: .debug, + configuration: config, buildSystem: buildSystem, ).stdout #expect(defaultOutput.contains(swiftCompilerPath.pathString)) @@ -816,14 +930,14 @@ struct BuildCommandTestCases { // we need to set the executable to use for the manifest itself to the default one, since it defaults to // SWIFT_EXEC if not provided. let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( + try await execute( ["--vv"], environment: [ "SWIFT_EXEC": "/usr/bin/false", "SWIFT_EXEC_MANIFEST": swiftCompilerPath.pathString, ], packagePath: fixturePath, - configuration: .debug, + configuration: config, buildSystem: buildSystem, ) } @@ -840,12 +954,13 @@ struct BuildCommandTestCases { } @Test( - arguments: SupportedBuildSystemOnPlatform, BuildConfiguration.allCases + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func printLLBuildManifestJobGraph( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration + data: BuildData, ) async throws { + let buildSystem = data.buildSystem + let configuration = data.config try await fixture(name: "DependencyResolution/Internal/Simple") { fixturePath in let output = try await execute( ["--print-manifest-job-graph"], @@ -895,12 +1010,13 @@ struct BuildCommandTestCases { @Test( .bug("https://github.com/swiftlang/swift-package-manager/issues/8659", "SWIFT_EXEC override is not working"), .SWBINTTODO("Test fails because the dummy-swiftc used in the test isn't accepted by swift-build. This needs to be investigated"), - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func swiftGetVersion( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { - + let buildSystem = data.buildSystem + let config = data.config try await fixture(name: "Miscellaneous/Simple") { fixturePath in func findSwiftGetVersionFile() throws -> AbsolutePath { let buildArenaPath = fixturePath.appending(components: ".build", "debug") @@ -918,13 +1034,20 @@ struct BuildCommandTestCases { "CUSTOM_SWIFT_VERSION": "1.0", ] - try await withKnownIssue("https://github.com/swiftlang/swift-package-manager/issues/8659, SWIFT_EXEC override is not working"){ - - + try await withKnownIssue( + "https://github.com/swiftlang/swift-package-manager/issues/8659, SWIFT_EXEC override is not working", + isIntermittent: (buildSystem == .native && config == .release) + ){ // Build with a swiftc that returns version 1.0, we expect a successful build which compiles our one source // file. do { - let result = try await execute(["--verbose"], environment: environment, packagePath: fixturePath, buildSystem: buildSystem) + let result = try await execute( + ["--verbose"], + environment: environment, + packagePath: fixturePath, + configuration: config, + buildSystem: buildSystem, + ) #expect( result.stdout.contains("\(dummySwiftcPath.pathString) -module-name"), "compilation task missing from build result: \(result.stdout)", @@ -941,7 +1064,13 @@ struct BuildCommandTestCases { // Build again with that same version, we do not expect any compilation tasks. do { - let result = try await execute(["--verbose"], environment: environment, packagePath: fixturePath, buildSystem: buildSystem) + let result = try await execute( + ["--verbose"], + environment: environment, + packagePath: fixturePath, + configuration: config, + buildSystem: buildSystem, + ) #expect( !result.stdout.contains("\(dummySwiftcPath.pathString) -module-name"), "compilation task present in build result: \(result.stdout)", @@ -959,7 +1088,13 @@ struct BuildCommandTestCases { // Build again with a swiftc that returns version 2.0, we expect compilation happening once more. do { environment["CUSTOM_SWIFT_VERSION"] = "2.0" - let result = try await execute(["--verbose"], environment: environment, packagePath: fixturePath, buildSystem: buildSystem) + let result = try await execute( + ["--verbose"], + environment: environment, + packagePath: fixturePath, + configuration: config, + buildSystem: buildSystem, + ) #expect( result.stdout.contains("\(dummySwiftcPath.pathString) -module-name"), "compilation task missing from build result: \(result.stdout)", @@ -974,7 +1109,9 @@ struct BuildCommandTestCases { #expect(actualVersion == "2.0") } } when: { - (ProcessInfo.hostOperatingSystem == .windows) || ([.xcode, .swiftbuild].contains(buildSystem)) + (ProcessInfo.hostOperatingSystem == .windows) + || ([.xcode, .swiftbuild].contains(buildSystem)) + || (buildSystem == .native && config == .release) } } } @@ -991,18 +1128,18 @@ struct BuildCommandTestCases { #if os(macOS) // try await building with default parameters. This should succeed. We build verbosely so we get full command // lines. - var buildResult = try await build(["-v"], packagePath: fixturePath, buildSystem: buildSystem,) + var buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .debug, buildSystem: buildSystem,) // TODO verification of the ad-hoc code signing can be done by `swift run` of the executable in these cases once swiftbuild build system is working with that #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) - buildResult = try await self.build(["-v"], packagePath: fixturePath, configuration:.debug, buildSystem: buildSystem,) + buildResult = try await build(["-v"], packagePath: fixturePath, configuration:.debug, buildSystem: buildSystem,) #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) // Build with different combinations of the entitlement flag and debug/release build configurations. - buildResult = try await self.build( + buildResult = try await build( ["--enable-get-task-allow-entitlement", "-v"], packagePath: fixturePath, configuration: .release, @@ -1011,7 +1148,7 @@ struct BuildCommandTestCases { #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) - buildResult = try await self.build( + buildResult = try await build( ["--enable-get-task-allow-entitlement", "-v"], packagePath: fixturePath, configuration: .debug, @@ -1020,7 +1157,7 @@ struct BuildCommandTestCases { #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) - buildResult = try await self.build( + buildResult = try await build( ["--disable-get-task-allow-entitlement", "-v"], packagePath: fixturePath, configuration: .debug, @@ -1029,7 +1166,7 @@ struct BuildCommandTestCases { #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - buildResult = try await self.build( + buildResult = try await build( ["--disable-get-task-allow-entitlement", "-v"], packagePath: fixturePath, configuration: .release, @@ -1038,15 +1175,15 @@ struct BuildCommandTestCases { #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) #else - var buildResult = try await self.build(["-v"], packagePath: fixturePath, buildSystem: buildSystem,) + var buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .debug, buildSystem: buildSystem,) #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - buildResult = try await self.build(["-v"], packagePath: fixturePath, configuration: .release,buildSystem: buildSystem,) + buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .release,buildSystem: buildSystem,) #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - buildResult = try await self.build( + buildResult = try await build( ["--disable-get-task-allow-entitlement", "-v"], packagePath: fixturePath, configuration: .release, @@ -1056,7 +1193,7 @@ struct BuildCommandTestCases { #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) #expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning)) - buildResult = try await self.build( + buildResult = try await build( ["--enable-get-task-allow-entitlement", "-v"], packagePath: fixturePath, configuration: .release, @@ -1067,7 +1204,7 @@ struct BuildCommandTestCases { #expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning)) #endif - buildResult = try await self.build(["-v"], packagePath: fixturePath, configuration: .release, buildSystem: buildSystem) + buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .release, buildSystem: buildSystem) #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) } @@ -1079,229 +1216,134 @@ struct BuildCommandTestCases { @Test( .requireHostOS(.linux), .SWBINTTODO("Swift build doesn't currently ignore Linux main when linking on Linux. This needs further investigation."), - arguments: SupportedBuildSystemOnPlatform, BuildConfiguration.allCases, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func ignoresLinuxMain( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration, + data: BuildData, ) async throws { - try await withKnownIssue { - try await fixture(name: "Miscellaneous/TestDiscovery/IgnoresLinuxMain") { fixturePath in - let buildResult = try await self.build( - ["-v", "--build-tests", "--enable-test-discovery"], - packagePath: fixturePath, - configuration: configuration, - cleanAfterward: false, - buildSystem: buildSystem, - ) - let testBinaryPath = buildResult.binPath.appending("IgnoresLinuxMainPackageTests.xctest") + let buildSystem = data.buildSystem + let configuration = data.config + try await fixture(name: "Miscellaneous/TestDiscovery/IgnoresLinuxMain") { fixturePath in + let buildResult = try await build( + ["-v", "--build-tests", "--enable-test-discovery"], + packagePath: fixturePath, + configuration: configuration, + cleanAfterward: false, + buildSystem: buildSystem, + ) + let testBinaryPath = buildResult.binPath.appending("IgnoresLinuxMainPackageTests.xctest") - _ = try await AsyncProcess.checkNonZeroExit(arguments: [testBinaryPath.pathString]) + switch buildSystem { + case .native: + expectFileExists(at: testBinaryPath) + _ = try await AsyncProcess.checkNonZeroExit(arguments: [testBinaryPath.pathString]) + case .swiftbuild: + // there are no additional check + break + case .xcode: + Issue.record("Test expectations have not been implemented.") } - } when: { - buildSystem == .swiftbuild } } - private static func buildSystemAndOutputLocation() throws -> [(BuildSystemProvider.Kind, Basics.RelativePath)] { - return try SupportedBuildSystemOnPlatform.map { buildSystem in + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnPlatform),[ + ["--verbose"], + ["-Xswiftc", "-diagnostic-style=llvm"], + ] + ) + func doesNotRebuildWithFlags( + data: BuildData, + flags: [String], + ) async throws { + func buildSystemAndOutputLocation( + buildSystem: BuildSystemProvider.Kind, + configuration: BuildConfiguration, + ) throws -> Basics.RelativePath { let triple = try UserToolchain.default.targetTriple.withoutVersion() let base = try RelativePath(validating: ".build") - let path = try base.appending(components: buildSystem.binPath(for: .debug, scratchPath: [])) + let path = try base.appending(components: buildSystem.binPath(for: configuration, scratchPath: [])) switch buildSystem { case .xcode: - return ( - buildSystem, - triple.platformName() == "macosx" ? path.appending("ExecutableNew") : path + return triple.platformName() == "macosx" ? path.appending("ExecutableNew") : path .appending("ExecutableNew.swiftmodule") .appending("Project") .appending("\(triple).swiftsourceinfo") - ) case .swiftbuild: - return ( - buildSystem, - triple.platformName() == "macosx" ? path.appending("ExecutableNew") : path + return triple.platformName() == "macosx" ? path.appending("ExecutableNew") : path .appending("ExecutableNew.swiftmodule") .appending("Project") .appending("\(triple).swiftsourceinfo") - ) case .native: - return ( - buildSystem, - path.appending("ExecutableNew.build") + return path.appending("ExecutableNew.build") .appending("main.swift.o") - ) - } - } - } - - @Test(arguments: try buildSystemAndOutputLocation()) - func doesNotRebuildWithVerboseFlag( - buildSystem: BuildSystemProvider.Kind, - outputFile: Basics.RelativePath - ) async throws { - try await withKnownIssue("Sometimes failed to build due to a possible path issue", isIntermittent: true) { - try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in - _ = try await self.build( - [], - packagePath: fixturePath, - cleanAfterward: false, - buildSystem: buildSystem, - ) - - let mainOFile = fixturePath.appending(outputFile) - let initialMainOMtime = try FileManager.default.attributesOfItem(atPath: mainOFile.pathString)[.modificationDate] as? Date - - _ = try await self.build( - ["--verbose"], - packagePath: fixturePath, - cleanAfterward: false, - buildSystem: buildSystem, - ) - - let subsequentMainOMtime = try FileManager.default.attributesOfItem(atPath: mainOFile.pathString)[.modificationDate] as? Date - #expect(initialMainOMtime == subsequentMainOMtime, "Expected no rebuild to occur when using the verbose flag, but the file was modified.") } - } when: { - buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .windows } - } - @Test(arguments: try buildSystemAndOutputLocation()) - func doesNotRebuildWithSwiftcArgsThatDontAffectIncrementalBuilds( - buildSystem: BuildSystemProvider.Kind, - outputFile: Basics.RelativePath - ) async throws { try await withKnownIssue("Sometimes failed to build due to a possible path issue", isIntermittent: true) { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in - _ = try await self.build( + _ = try await build( [], packagePath: fixturePath, + configuration: data.config, cleanAfterward: false, - buildSystem: buildSystem, + buildSystem: data.buildSystem, ) - - let mainOFile = fixturePath.appending(outputFile) + let mainOFile = try fixturePath.appending(buildSystemAndOutputLocation(buildSystem: data.buildSystem, configuration: data.config)) let initialMainOMtime = try FileManager.default.attributesOfItem(atPath: mainOFile.pathString)[.modificationDate] as? Date - _ = try await self.build( - ["-Xswiftc", "-diagnostic-style=llvm"], + _ = try await build( + flags, packagePath: fixturePath, + configuration: data.config, cleanAfterward: false, - buildSystem: buildSystem, + buildSystem: data.buildSystem, ) let subsequentMainOMtime = try FileManager.default.attributesOfItem(atPath: mainOFile.pathString)[.modificationDate] as? Date - #expect(initialMainOMtime == subsequentMainOMtime, "Expected no rebuild to occur when supplying -diagnostic-style, but the file was modified.") - } - } when: { - buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .windows - } - } - - @Test( - .SWBINTTODO("Test failed because of missing plugin support in the PIF builder. This can be reinvestigated after the support is there."), - .tags( - Tag.Feature.CodeCoverage, - Tag.Feature.Command.Test, - ), - arguments: SupportedBuildSystemOnPlatform, - ) - func executingTestsWithCoverageWithoutCodeBuiltWithCoverageGeneratesAFailure( - buildSystem: BuildSystemProvider.Kind, - ) async throws { - try await withKnownIssue(isIntermittent: (ProcessInfo.hostOperatingSystem == .linux && buildSystem == .swiftbuild)) { - try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in - _ = try await self.build( - ["--build-tests"], - packagePath: path, - cleanAfterward: false, - buildSystem: buildSystem, - ) - await #expect(throws: (any Error).self ) { - try await executeSwiftTest( - path, - extraArgs: [ - "--skip-build", - "--enable-code-coverage", - ], - throwIfCommandFails: true, - buildSystem: buildSystem, - ) - } + #expect(initialMainOMtime == subsequentMainOMtime, "Expected no rebuild to occur when using flags \(flags), but the file was modified.") } } when: { - buildSystem == .xcode || (buildSystem == .swiftbuild && [.linux, .windows].contains(ProcessInfo.hostOperatingSystem)) + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .windows } } @Test( - .SWBINTTODO("Test failed because of missing plugin support in the PIF builder. This can be reinvestigated after the support is there."), - .tags( - Tag.Feature.CodeCoverage, - Tag.Feature.Command.Test, - ), - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) - func executingTestsWithCoverageWithCodeBuiltWithCoverageGeneratesCodecove( - buildSystem: BuildSystemProvider.Kind, + func parseAsLibraryCriteria( + buildData: BuildData, ) async throws { - // Test that enabling code coverage during building produces the expected folder. try await withKnownIssue { - try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in - let buildResult = try await self.build( - ["--build-tests", "--enable-code-coverage"], - packagePath: path, - cleanAfterward: false, - buildSystem: buildSystem, - ) - try await executeSwiftTest( - path, - extraArgs: [ - "--skip-build", - "--enable-code-coverage", - ], - throwIfCommandFails: true, - buildSystem: buildSystem, - ) - let codeCovPath = buildResult.binPath.appending("codecov") - let codeCovFiles = try localFileSystem.getDirectoryContents(codeCovPath) - #expect(codeCovFiles.count > 0) - } - } when: { - [.swiftbuild, .xcode].contains(buildSystem) - } - } - - @Test(arguments: [BuildSystemProvider.Kind.native, .swiftbuild]) - func parseAsLibraryCriteria(buildSystem: BuildSystemProvider.Kind) async throws { - try await withKnownIssue { - try await fixture(name: "Miscellaneous/ParseAsLibrary") { fixturePath in + try await fixture(name: "Miscellaneous/ParseAsLibrary") { fixturePath in _ = try await executeSwiftBuild( fixturePath, - buildSystem: buildSystem, + configuration: buildData.config, + buildSystem: buildData.buildSystem, throwIfCommandFails: true ) } - } when: { - ProcessInfo.hostOperatingSystem == .windows && - buildSystem == .swiftbuild - } + } when: { + ProcessInfo.hostOperatingSystem == .windows && + buildData.buildSystem == .swiftbuild + } } @Test( - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func fatalErrorDisplayedCorrectNumberOfTimesWhenSingleXCTestHasFatalErrorInBuildCompilation( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { let expected = 0 try await fixture(name: "Miscellaneous/Errors/FatalErrorInSingleXCTest/TypeLibrary") { fixturePath in // WHEN swift-build --build-tests is executed" let error = await #expect(throws: SwiftPMError.self ) { - try await self.execute( + try await execute( ["--build-tests"], - packagePath: fixturePath,buildSystem: buildSystem, + packagePath: fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, ) } // THEN I expect a failure @@ -1321,13 +1363,14 @@ struct BuildCommandTestCases { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8844"), - arguments: SupportedBuildSystemOnPlatform, BuildConfiguration.allCases + .issue("https://github.com/swiftlang/swift-package-manager/issues/8844", relationship: .defect), + arguments: getBuildData(for: SupportedBuildSystemOnPlatform), ) func swiftBuildQuietLogLevel( - buildSystem: BuildSystemProvider.Kind, - configuration: BuildConfiguration + data: BuildData, ) async throws { + let buildSystem = data.buildSystem + let configuration = data.config try await withKnownIssue { // GIVEN we have a simple test package try await fixture(name: "Miscellaneous/SwiftBuild") { fixturePath in @@ -1350,7 +1393,7 @@ struct BuildCommandTestCases { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8844"), + .issue("https://github.com/swiftlang/swift-package-manager/issues/8844", relationship: .defect), arguments: SupportedBuildSystemOnPlatform, BuildConfiguration.allCases ) func swiftBuildQuietLogLevelWithError( @@ -1383,17 +1426,18 @@ struct BuildCommandTestCases { return } - if buildSystem == .swiftbuild { - // THEN we should see output in stderr - #expect(stderr.isEmpty == false) - // AND no content in stdout - #expect(stdout.isEmpty) - } else { - // THEN we should see content in stdout - #expect(stdout.isEmpty == false) - // AND no output in stderr - #expect(stderr.isEmpty) - } + switch buildSystem { + case .swiftbuild: + // THEN we should see output in stderr + #expect(stderr.isEmpty == false) + // AND no content in stdout + #expect(stdout.isEmpty) + case .native, .xcode: + // THEN we should see content in stdout + #expect(stdout.isEmpty == false) + // AND no output in stderr + #expect(stderr.isEmpty) + } } } } diff --git a/Tests/CommandsTests/CoverageTests.swift b/Tests/CommandsTests/CoverageTests.swift new file mode 100644 index 00000000000..8881450e06b --- /dev/null +++ b/Tests/CommandsTests/CoverageTests.swift @@ -0,0 +1,172 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Foundation +import Commands +import _InternalTestSupport +import var Basics.localFileSystem +import struct Basics.AbsolutePath +import enum PackageModel.BuildConfiguration +import struct SPMBuildCore.BuildSystemProvider +import Testing + +@Suite( + .tags( + .TestSize.large, + .Feature.CodeCoverage, + ) +) +struct CoverageTests { + @Test( + .SWBINTTODO("Test failed because of missing plugin support in the PIF builder. This can be reinvestigated after the support is there."), + .tags( + Tag.Feature.CodeCoverage, + Tag.Feature.Command.Test, + ), + arguments: SupportedBuildSystemOnAllPlatforms, + ) + func executingTestsWithCoverageWithoutCodeBuiltWithCoverageGeneratesAFailure( + buildSystem: BuildSystemProvider.Kind, + ) async throws { + let config = BuildConfiguration.debug + try await withKnownIssue(isIntermittent: (ProcessInfo.hostOperatingSystem == .linux && buildSystem == .swiftbuild)) { + try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in + _ = try await executeSwiftBuild( + path, + configuration: config, + extraArgs: ["--build-tests"], + buildSystem: buildSystem, + ) + await #expect(throws: (any Error).self ) { + try await executeSwiftTest( + path, + configuration: config, + extraArgs: [ + "--skip-build", + "--enable-code-coverage", + ], + throwIfCommandFails: true, + buildSystem: buildSystem, + ) + } + } + } when: { + buildSystem == .swiftbuild && [.linux, .windows].contains(ProcessInfo.hostOperatingSystem) + } + } + + @Test( + .SWBINTTODO("Test failed because of missing plugin support in the PIF builder. This can be reinvestigated after the support is there."), + .IssueWindowsCannotSaveAttachment, + .tags( + Tag.Feature.CodeCoverage, + Tag.Feature.Command.Test, + ), + arguments: SupportedBuildSystemOnAllPlatforms, + ) + func executingTestsWithCoverageWithCodeBuiltWithCoverageGeneratesCodeCoverage( + buildSystem: BuildSystemProvider.Kind, + ) async throws { + let config = BuildConfiguration.debug + // Test that enabling code coverage during building produces the expected folder. + try await withKnownIssue(isIntermittent: true) { + try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in + let codeCovPathString = try await executeSwiftTest( + path, + configuration: config, + extraArgs: [ + "--show-coverage-path", + ], + throwIfCommandFails: true, + buildSystem: buildSystem, + ).stdout.trimmingCharacters(in: .whitespacesAndNewlines) + + let codeCovPath = try AbsolutePath(validating: codeCovPathString) + + // WHEN we build with coverage enabled + try await withKnownIssue { + try await executeSwiftBuild( + path, + configuration: config, + extraArgs: ["--build-tests", "--enable-code-coverage"], + buildSystem: buildSystem, + ) + + // AND we test with coverag enabled and skip the build + try await executeSwiftTest( + path, + configuration: config, + extraArgs: [ + "--skip-build", + "--enable-code-coverage", + ], + buildSystem: buildSystem, + ) + + // THEN we expect the file to exists + expectFileExists(at: codeCovPath) + + // AND the parent directory is non empty + let codeCovFiles = try localFileSystem.getDirectoryContents(codeCovPath.parentDirectory) + #expect(codeCovFiles.count > 0) + } when: { + ProcessInfo.hostOperatingSystem == .linux && buildSystem == .swiftbuild + } + } + } when: { + ProcessInfo.hostOperatingSystem == .windows && buildSystem == .swiftbuild + } + } + + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), [ + "Coverage/Simple", + "Miscellaneous/TestDiscovery/Simple", + ], + ) + func generateCoverageReport( + buildData: BuildData, + fixtureName: String + ) async throws { + try await fixture(name: fixtureName) { path in + let coveragePathString = try await executeSwiftTest( + path, + configuration: buildData.config, + extraArgs: [ + "--show-coverage-path", + ], + throwIfCommandFails: true, + buildSystem: buildData.buildSystem, + ).stdout + let coveragePath = try AbsolutePath(validating: coveragePathString) + try #require(!localFileSystem.exists(coveragePath)) + + // WHEN we test with coverage enabled + try await withKnownIssue { + try await executeSwiftTest( + path, + configuration: buildData.config, + extraArgs: [ + "--enable-code-coverage", + ], + throwIfCommandFails: true, + buildSystem: buildData.buildSystem, + ) + + // THEN we expect the file to exists + #expect(localFileSystem.exists(coveragePath)) + } when: { + (buildData.buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) + } + } + } +} diff --git a/Tests/CommandsTests/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index d282ab16002..1eee96e52a2 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -91,8 +91,8 @@ struct RunCommandTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .SWBINTTODO("Test package fails to build on Windows"), arguments: SupportedBuildSystemOnPlatform, ) @@ -135,8 +135,8 @@ struct RunCommandTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, arguments: SupportedBuildSystemOnPlatform, ) func productArgumentPassing( @@ -237,8 +237,8 @@ struct RunCommandTests { @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, arguments: SupportedBuildSystemOnPlatform, ) func unreachableExecutable( diff --git a/Tests/CommandsTests/Sanitizer+ExtensionsTests.swift b/Tests/CommandsTests/Sanitizer+ExtensionsTests.swift new file mode 100644 index 00000000000..c7008f269b1 --- /dev/null +++ b/Tests/CommandsTests/Sanitizer+ExtensionsTests.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Testing +import enum PackageModel.Sanitizer + +@Suite( + .tags( + Tag.TestSize.small, + ), +) +struct SanitizerExtensionTests { + @Test( + arguments: Sanitizer.allCases + ) + func creatingSanitizers(sanitizer: Sanitizer) throws { + #expect(sanitizer == Sanitizer(argument: sanitizer.shortName)) + } + + @Test + func invalidSanitizer() throws { + #expect(Sanitizer(argument: "invalid") == nil) + } +} diff --git a/Tests/CommandsTests/TestCommandTests.swift b/Tests/CommandsTests/TestCommandTests.swift index b7684acbd77..45815324d3b 100644 --- a/Tests/CommandsTests/TestCommandTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -1065,7 +1065,7 @@ struct TestCommandTests { } @Test( - .issue("https://github.com/swiftlang/swift-package-manager/issues/8511", relationship: .defect), + .IssueWindowsPathTestsFailures, .issue("https://github.com/swiftlang/swift-package-manager/issues/8602", relationship: .defect), arguments: SupportedBuildSystemOnAllPlatforms, BuildConfiguration.allCases, ) diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index 67969ddd2fa..edb30021eec 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -30,7 +30,7 @@ import Foundation ) final class PluginTests { @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8791"), .requiresSwiftConcurrencySupport, ) @@ -67,7 +67,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8786"), .requiresSwiftConcurrencySupport, ) @@ -98,7 +98,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), .requiresSwiftConcurrencySupport, ) @@ -137,7 +137,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), .requiresSwiftConcurrencySupport, ) @@ -176,7 +176,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), .requiresSwiftConcurrencySupport, ) @@ -247,7 +247,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), .requiresSwiftConcurrencySupport, ) @@ -284,7 +284,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), .bug("https://github.com/swiftlang/swift-package-manager/issues/8791"), .requiresSwiftConcurrencySupport, @@ -320,7 +320,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), .requiresSwiftConcurrencySupport, ) @@ -428,7 +428,7 @@ final class PluginTests { @Test( .bug("https://github.com/swiftlang/swift-package-manager/issues/8794"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .requiresSwiftConcurrencySupport, arguments: SupportedBuildSystemOnAllPlatforms, ) @@ -866,7 +866,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .requiresSwiftConcurrencySupport, arguments: [BuildSystemProvider.Kind.native, .swiftbuild] ) @@ -1361,7 +1361,7 @@ final class PluginTests { struct SnippetTests { @Test( .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .requiresSwiftConcurrencySupport, arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) @@ -1583,7 +1583,7 @@ final class PluginTests { @Test( .bug("https://github.com/swiftlang/swift-package-manager/issues/8774"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .requiresSwiftConcurrencySupport, arguments: SupportedBuildSystemOnAllPlatforms, ) @@ -1630,7 +1630,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8791"), .requiresSwiftConcurrencySupport, ) @@ -1666,7 +1666,7 @@ final class PluginTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsRelativePathAssert, .bug("https://github.com/swiftlang/swift-package-manager/issues/8791"), .requiresSwiftConcurrencySupport, ) diff --git a/Tests/FunctionalTests/TraitTests.swift b/Tests/FunctionalTests/TraitTests.swift index d590e0ca62c..422715b6433 100644 --- a/Tests/FunctionalTests/TraitTests.swift +++ b/Tests/FunctionalTests/TraitTests.swift @@ -27,8 +27,8 @@ import _InternalTestSupport ) struct TraitTests { @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -71,8 +71,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -124,8 +124,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -174,8 +174,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -228,8 +228,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -269,8 +269,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -315,8 +315,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -372,8 +372,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .IssueSwiftBuildLinuxRunnable, .IssueProductTypeForObjectLibraries, .tags( @@ -457,8 +457,8 @@ struct TraitTests { } @Test( - .bug("https://github.com/swiftlang/swift-package-manager/issues/8511"), - .bug("https://github.com/swiftlang/swift-package-manager/issues/8602"), + .IssueWindowsPathTestsFailures, + .IssueWindowsRelativePathAssert, .tags( Tag.Feature.Command.Test, ), diff --git a/Tests/IntegrationTests/SwiftPMTests.swift b/Tests/IntegrationTests/SwiftPMTests.swift index 3cc2d367e31..30adb72c78d 100644 --- a/Tests/IntegrationTests/SwiftPMTests.swift +++ b/Tests/IntegrationTests/SwiftPMTests.swift @@ -13,7 +13,9 @@ import _IntegrationTestSupport import _InternalTestSupport import Testing import Basics +import enum PackageModel.BuildConfiguration import struct SPMBuildCore.BuildSystemProvider + @Suite( .tags(Tag.TestSize.large) ) @@ -224,20 +226,28 @@ private struct SwiftPMTests { } } - @Test(.requireSwift6_2) - func testCodeCoverageMergedAcrossSubprocesses() async throws { - try await withTemporaryDirectory { tmpDir in + @Test( + .requireSwift6_2, + arguments: SupportedBuildSystemOnAllPlatforms + ) + func testCodeCoverageMergedAcrossSubprocesses( + buildSystem: BuildSystemProvider.Kind, + ) async throws { + let config = BuildConfiguration.debug + try await withTemporaryDirectory(removeTreeOnDeinit: false) { tmpDir in let packagePath = tmpDir.appending(component: "test-package-coverage") try localFileSystem.createDirectory(packagePath) try await executeSwiftPackage( packagePath, + configuration: config, extraArgs: ["init", "--type", "empty"], - buildSystem: .native, + buildSystem: buildSystem, ) try await executeSwiftPackage( packagePath, + configuration: config, extraArgs: ["add-target", "--type", "test", "ReproTests"], - buildSystem: .native, + buildSystem: buildSystem, ) try localFileSystem.writeFileContents( AbsolutePath(validating: "Tests/ReproTests/Subject.swift", relativeTo: packagePath), @@ -265,66 +275,73 @@ private struct SwiftPMTests { ) let expectedCoveragePath = try await executeSwiftTest( packagePath, + configuration: config, extraArgs: ["--show-coverage-path"], - buildSystem: .native, + buildSystem: buildSystem, ).stdout.trimmingCharacters(in: .whitespacesAndNewlines) try await executeSwiftTest( packagePath, + configuration: config, extraArgs: ["--enable-code-coverage", "--disable-xctest"], - buildSystem: .native, + buildSystem: buildSystem, ) let coveragePath = try AbsolutePath(validating: expectedCoveragePath) // Check the coverage path exists. - #expect(localFileSystem.exists(coveragePath)) + try withKnownIssue { + // the CoveragePath file does not exists in Linux platform build + expectFileExists(at: coveragePath) - // This resulting coverage file should be merged JSON, with a schema that valiades against this subset. - struct Coverage: Codable { - var data: [Entry] - struct Entry: Codable { - var files: [File] - struct File: Codable { - var filename: String - var summary: Summary - struct Summary: Codable { - var functions: Functions - struct Functions: Codable { - var count, covered: Int - var percent: Double + // This resulting coverage file should be merged JSON, with a schema that valiades against this subset. + struct Coverage: Codable { + var data: [Entry] + struct Entry: Codable { + var files: [File] + struct File: Codable { + var filename: String + var summary: Summary + struct Summary: Codable { + var functions: Functions + struct Functions: Codable { + var count, covered: Int + var percent: Double + } } } } } - } - let coverageJSON = try localFileSystem.readFileContents(coveragePath) - let coverage = try JSONDecoder().decode(Coverage.self, from: Data(coverageJSON.contents)) + let coverageJSON = try localFileSystem.readFileContents(coveragePath) + let coverage = try JSONDecoder().decode(Coverage.self, from: Data(coverageJSON.contents)) - // Check for 100% coverage for Subject.swift, which should happen because the per-PID files got merged. - let subjectCoverage = coverage.data.first?.files.first(where: { $0.filename.hasSuffix("Subject.swift") }) - #expect(subjectCoverage?.summary.functions.count == 2) - #expect(subjectCoverage?.summary.functions.covered == 2) - #expect(subjectCoverage?.summary.functions.percent == 100) + // Check for 100% coverage for Subject.swift, which should happen because the per-PID files got merged. + let subjectCoverage = try #require(coverage.data.first?.files.first(where: { $0.filename.hasSuffix("Subject.swift") })) + #expect(subjectCoverage.summary.functions.count == 2) + #expect(subjectCoverage.summary.functions.covered == 2) + #expect(subjectCoverage.summary.functions.percent == 100) - // Check the directory with the coverage path contains the profraw files. - let coverageDirectory = coveragePath.parentDirectory - let coverageDirectoryContents = try localFileSystem.getDirectoryContents(coverageDirectory) + // Check the directory with the coverage path contains the profraw files. + let coverageDirectory = coveragePath.parentDirectory + let coverageDirectoryContents = try localFileSystem.getDirectoryContents(coverageDirectory) - // SwiftPM uses an LLVM_PROFILE_FILE that ends with ".%p.profraw", which we validated in the test above. - // Let's first check all the files have the expected extension. - let profrawFiles = coverageDirectoryContents.filter { $0.hasSuffix(".profraw") } + // SwiftPM uses an LLVM_PROFILE_FILE that ends with ".%p.profraw", which we validated in the test above. + // Let's first check all the files have the expected extension. + let profrawFiles = coverageDirectoryContents.filter { $0.hasSuffix(".profraw") } - // Then check that %p expanded as we expected: to something that plausibly looks like a PID. - for profrawFile in profrawFiles { - let shouldBePID = try #require(profrawFile.split(separator: ".").dropLast().last) - #expect(Int(shouldBePID) != nil) - } + // Then check that %p expanded as we expected: to something that plausibly looks like a PID. + for profrawFile in profrawFiles { + let shouldBePID = try #require(profrawFile.split(separator: ".").dropLast().last) + #expect(Int(shouldBePID) != nil) + } - // Group the files by binary identifier (have a different prefix, before the per-PID suffix). - let groups = Dictionary(grouping: profrawFiles) { path in path.split(separator: ".").dropLast(2) }.values + // Group the files by binary identifier (have a different prefix, before the per-PID suffix). + let groups = Dictionary(grouping: profrawFiles) { path in path.split(separator: ".").dropLast(2) }.values - // Check each group has 3 files: one per PID (the above suite has 2 exit tests => 2 forks => 3 PIDs total). - for binarySpecificProfrawFiles in groups { - #expect(binarySpecificProfrawFiles.count == 3) + // Check each group has 3 files: one per PID (the above suite has 2 exit tests => 2 forks => 3 PIDs total). + for binarySpecificProfrawFiles in groups { + #expect(binarySpecificProfrawFiles.count == 3) + } + } when: { + ProcessInfo.hostOperatingSystem == .linux && buildSystem == .swiftbuild } } } diff --git a/Tests/PackageGraphTests/ModulesGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift index 55699e5c739..7145a61ea3d 100644 --- a/Tests/PackageGraphTests/ModulesGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -113,7 +113,7 @@ struct ModulesGraphTests { } @Test( - .issue("https://github.com/swiftlang/swift-package-manager/issues/8511", relationship: .defect), + .IssueWindowsPathTestsFailures, ) func basic() throws { try withKnownIssue { From 97b623f71745aecd170387c308407a94bb0b8555 Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Thu, 2 Oct 2025 12:07:21 -0400 Subject: [PATCH 2/2] PIF: Ensure targets are created Some PIF target creation were guarded behind a tool versioin. Remove this guard to ensure modules can be build using `--target` with the Swift Build build system. Fixes: #9138 Issue: rdar://160638539 --- .../MultipleExecutables/Package.swift | 4 +- .../Sources/exec1/main.swift | 3 ++ .../Sources/exec2/main.swift | 3 ++ .../Sources/lib1/lib.swift | 3 ++ .../PackagePIFProjectBuilder+Modules.swift | 1 - Tests/CommandsTests/BuildCommandTests.swift | 51 ++++++++++++------- 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/Fixtures/Miscellaneous/MultipleExecutables/Package.swift b/Fixtures/Miscellaneous/MultipleExecutables/Package.swift index 70ced080ae1..436ca709dc4 100644 --- a/Fixtures/Miscellaneous/MultipleExecutables/Package.swift +++ b/Fixtures/Miscellaneous/MultipleExecutables/Package.swift @@ -9,8 +9,8 @@ let package = Package( .library(name: "lib1", targets: ["lib1"]), ], targets: [ - .target(name: "exec1"), - .target(name: "exec2"), + .target(name: "exec1", dependencies: ["lib1"]), + .target(name: "exec2", dependencies: ["lib1"]), .target(name: "lib1"), ] ) \ No newline at end of file diff --git a/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec1/main.swift b/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec1/main.swift index 32aa4e661ca..b0ea8bb27a9 100644 --- a/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec1/main.swift +++ b/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec1/main.swift @@ -1 +1,4 @@ +import lib1 + +foo() print("1") \ No newline at end of file diff --git a/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec2/main.swift b/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec2/main.swift index 57e96ca2d31..06364887d90 100644 --- a/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec2/main.swift +++ b/Fixtures/Miscellaneous/MultipleExecutables/Sources/exec2/main.swift @@ -1 +1,4 @@ +import lib1 + +foo() print("2") \ No newline at end of file diff --git a/Fixtures/Miscellaneous/MultipleExecutables/Sources/lib1/lib.swift b/Fixtures/Miscellaneous/MultipleExecutables/Sources/lib1/lib.swift index e69de29bb2d..e418a8155df 100644 --- a/Fixtures/Miscellaneous/MultipleExecutables/Sources/lib1/lib.swift +++ b/Fixtures/Miscellaneous/MultipleExecutables/Sources/lib1/lib.swift @@ -0,0 +1,3 @@ +public func foo() { + print("In lib") +} \ No newline at end of file diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift index 727562b27dd..e46c7788354 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift @@ -199,7 +199,6 @@ extension PackagePIFProjectBuilder { /// we also construct a testable version of said executable. mutating func makeTestableExecutableSourceModule(_ executableModule: PackageGraph.ResolvedModule) throws { precondition(executableModule.type == .executable) - guard self.package.manifest.toolsVersion >= .v5_5 else { return } let inputResourceBundleName: String? = if mainModuleTargetNamesWithResources.contains(executableModule.name) { resourceBundleName(forModuleName: executableModule.name) diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index 1ee9ed8c50d..e15add25142 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -392,30 +392,45 @@ struct BuildCommandTestCases { @Test( .issue("https://github.com/swiftlang/swift-package-manager/issues/9138", relationship: .defect), - arguments: getBuildData(for: SupportedBuildSystemOnPlatform), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) func buildExistingTargetIsSuccessfull( data: BuildData, ) async throws { let buildSystem = data.buildSystem - try await withKnownIssue("Could not find target named 'exec2'") { - try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in - let fullPath = try resolveSymlinks(fixturePath) + try await fixture(name: "Miscellaneous/MultipleExecutables") { fixturePath in + let fullPath = try resolveSymlinks(fixturePath) - let result = try await build( - ["--target", "exec2"], - packagePath: fullPath, - configuration: data.config, - buildSystem: buildSystem, - ) - #expect(result.binContents.contains("exec2.build")) - #expect(!result.binContents.contains(executableName("exec1"))) + let result = try await build( + ["--target", "exec2"], + packagePath: fullPath, + configuration: data.config, + cleanAfterward: false, + buildSystem: buildSystem, + ) + switch buildSystem { + case .native: + #expect(result.binContents.contains("exec2.build")) + #expect(!result.binContents.contains(executableName("exec1"))) + case .swiftbuild: + // Ensure there is a single file in the bin directory + let binPathContents = try localFileSystem.getDirectoryContents(result.binPath).filter { fileName in + let path = result.binPath.appending(fileName) + return localFileSystem.isFile(path) + } + #expect(binPathContents.count == 1, "filtered contents: \(binPathContents)") + expectFileExists(at: result.binPath.appending("lib1.o")) + + // Verify the taget was built + let executableModulesDirectoryContents = try localFileSystem.getDirectoryContents(result.binPath.appending("ExecutableModules")) + try #require(executableModulesDirectoryContents.count == 1) + let content = executableModulesDirectoryContents.first! + #expect(content.hasPrefix("exec2")) + #expect(content.hasSuffix(".o")) + break + case .xcode: + Issue.record("Test expections have not bee specified") } - } when: { - [ - .swiftbuild, - .xcode, - ].contains(buildSystem) } } @@ -949,7 +964,7 @@ struct BuildCommandTestCases { #expect(stderr.contains("/usr/bin/false")) } } when: { - buildSystem == .swiftbuild + buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem) } }