diff --git a/Sources/Basics/Process.swift b/Sources/Basics/Process.swift index 9cc7f801daf..bd6541c6e1b 100644 --- a/Sources/Basics/Process.swift +++ b/Sources/Basics/Process.swift @@ -30,6 +30,8 @@ extension ProcessInfo { .windows #elseif os(FreeBSD) .freebsd + #elseif os(Android) + .android #else .unknown #endif diff --git a/Sources/_InternalTestSupport/BuildSystemProvider+Configuration.swift b/Sources/_InternalTestSupport/BuildSystemProvider+Configuration.swift index 30b3db4b2c7..97c8cfca90c 100644 --- a/Sources/_InternalTestSupport/BuildSystemProvider+Configuration.swift +++ b/Sources/_InternalTestSupport/BuildSystemProvider+Configuration.swift @@ -56,11 +56,7 @@ extension BuildSystemProvider.Kind { if let triple { tripleString = triple } else { - do { - tripleString = try UserToolchain.default.targetTriple.platformBuildPathComponent - } catch { - tripleString = "" - } + tripleString = try UserToolchain.default.targetTriple.platformBuildPathComponent } switch self { case .native: diff --git a/Sources/_InternalTestSupport/SwiftPMProduct.swift b/Sources/_InternalTestSupport/SwiftPMProduct.swift index 0608fe4ca47..e27951ead74 100644 --- a/Sources/_InternalTestSupport/SwiftPMProduct.swift +++ b/Sources/_InternalTestSupport/SwiftPMProduct.swift @@ -53,7 +53,7 @@ extension SwiftPM { } public var xctestBinaryPath: AbsolutePath { - Self.xctestBinaryPath(for: RelativePath("swift-package-manager")) + Self.xctestBinaryPath(for: RelativePath(self.executableName)) } public static func xctestBinaryPath(for executableName: RelativePath) -> AbsolutePath { diff --git a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift index 69e8b06fee0..aec2cd66f5e 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift @@ -18,7 +18,18 @@ public func expectFileExists( #expect( localFileSystem.exists(path), - "Files '\(path)' does not exist.", + "File '\(path)' does not exist.", + sourceLocation: sourceLocation, + ) +} + +public func requireFileExists( + at path: AbsolutePath, + sourceLocation: SourceLocation = #_sourceLocation, +) throws { + try #require( + localFileSystem.exists(path), + "File '\(path)' does not exist.", sourceLocation: sourceLocation, ) } diff --git a/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift b/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift index 99de658066a..2381032b871 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+TraitsBug.swift @@ -46,6 +46,13 @@ extension Trait where Self == Testing.Bug { ) } + public static var IssueWindowsAbsoluteAndRelativePathTestFailures: Self { + issue( + "https://github.com/swiftlang/swift-package-manager/issues/8511", + relationship: .defect, + ) + } + public static var IssueWindowsLongPath: Self { .issue( // "https://github.com/swiftlang/swift-tools-support-core/pull/521", @@ -69,6 +76,13 @@ extension Trait where Self == Testing.Bug { ) } + public static var IssueSwiftBuildSpaceInPath: Self { + .issue( + "https://github.com/swiftlang/swift-package-manager/issues/8400", + relationship: .defect, + ) + } + public static var IssueSwiftBuildLinuxRunnable: Self { .issue( "https://github.com/swiftlang/swift-package-manager/issues/8416", diff --git a/Tests/BuildTests/BuildSystemDelegateTests.swift b/Tests/BuildTests/BuildSystemDelegateTests.swift index 52108d01e62..a975f2db9b0 100644 --- a/Tests/BuildTests/BuildSystemDelegateTests.swift +++ b/Tests/BuildTests/BuildSystemDelegateTests.swift @@ -45,12 +45,13 @@ struct BuildSystemDelegateTests { "log didn't contain expected linker diagnostics. stderr: '\(stderr)')", ) case .swiftbuild: + let searchPathRegex = try Regex("warning:(.*)Search path 'foobar' not found") #expect( - stderr.contains("warning: Search path 'foobar' not found"), + stderr.contains(searchPathRegex), "log didn't contain expected linker diagnostics. stderr: '\(stdout)", ) #expect( - !stdout.contains("warning: Search path 'foobar' not found"), + !stdout.contains(searchPathRegex), "log didn't contain expected linker diagnostics. stderr: '\(stderr)')", ) case .xcode: diff --git a/Tests/FunctionalTests/MiscellaneousTests.swift b/Tests/FunctionalTests/MiscellaneousTests.swift index f10c6b65872..77d1ce71fa2 100644 --- a/Tests/FunctionalTests/MiscellaneousTests.swift +++ b/Tests/FunctionalTests/MiscellaneousTests.swift @@ -2,149 +2,241 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors +// Copyright (c) 2014-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 Basics import PackageModel import SourceControl import _InternalTestSupport import Workspace -import XCTest +import Testing import class Basics.AsyncProcess import enum TSCUtility.Git typealias ProcessID = AsyncProcess.ProcessID -final class MiscellaneousTestCase: XCTestCase { - func testPrintsSelectedDependencyVersion() async throws { - // verifies the stdout contains information about - // the selected version of the package - - try await fixtureXCTest(name: "DependencyResolution/External/Simple") { fixturePath in +@Suite( + .tags( + .TestSize.large, + ) +) +struct MiscellaneousTestCase { + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func printsSelectedDependencyVersion( + data: BuildData, + ) async throws { + try await fixture(name: "DependencyResolution/External/Simple") { fixturePath in let (stdout, stderr) = try await executeSwiftBuild( fixturePath.appending("Bar"), - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) // package resolution output goes to stderr - XCTAssertMatch(stderr, .regex("Computed .* at 1\\.2\\.3")) + let packageRegex = try Regex("Computed .* at 1\\.2\\.3") + #expect(stderr.contains(packageRegex)) // in "swift build" build output goes to stdout - XCTAssertMatch(stdout, .contains("Compiling Foo Foo.swift")) - XCTAssertMatch(stdout, .or(.contains("Merging module Foo"), - .contains("Emitting module Foo"))) - XCTAssertMatch(stdout, .contains("Compiling Bar main.swift")) - XCTAssertMatch(stdout, .or(.contains("Merging module Bar"), - .contains("Emitting module Bar"))) - XCTAssertMatch(stdout, .contains("Linking Bar")) - XCTAssertMatch(stdout, .contains("Build complete!")) + if data.buildSystem == .native { + #expect(stdout.contains("Compiling Foo Foo.swift")) + #expect(stdout.contains("Compiling Bar main.swift")) + if (data.config == .debug) { + #expect(stdout.contains("Merging module Foo") || stdout.contains("Emitting module Foo")) + #expect(stdout.contains("Merging module Bar") || stdout.contains("Emitting module Bar")) + } + #expect(stdout.contains("Linking Bar")) + } + #expect(stdout.contains("Build complete!")) } } - func testPassExactDependenciesToBuildCommand() async throws { - // regression test to ensure that dependencies of other dependencies - // are not passed into the build-command. - - try await fixtureXCTest(name: "Miscellaneous/ExactDependencies") { fixturePath in - await XCTAssertBuilds( - fixturePath.appending("app"), - buildSystem: .native, + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func passExactDependenciesToBuildCommand( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/ExactDependencies") { fixturePath in + let packagePath = fixturePath.appending("app") + try await executeSwiftBuild( + packagePath, + configuration: data.config, + buildSystem: data.buildSystem, ) - let buildDir = fixturePath.appending(components: "app", ".build", try UserToolchain.default.targetTriple.platformBuildPathComponent, "debug") - XCTAssertFileExists(buildDir.appending(executableName("FooExec"))) - XCTAssertFileExists(buildDir.appending(components: "Modules", "FooLib1.swiftmodule")) - XCTAssertFileExists(buildDir.appending(components: "Modules", "FooLib2.swiftmodule")) + let buildDir = try packagePath.appending(components: data.buildSystem.binPath(for: data.config)) + expectFileExists(at: buildDir.appending(executableName("FooExec"))) + if data.buildSystem == .native { + expectFileExists(at: buildDir.appending(components: "Modules", "FooLib1.swiftmodule")) + expectFileExists(at: buildDir.appending(components: "Modules", "FooLib2.swiftmodule")) + } } } - func testCanBuildMoreThanTwiceWithExternalDependencies() async throws { - // running `swift build` multiple times should not fail - // subsequent executions to an unmodified source tree - // should immediately exit with exit-status: `0` - - try await fixtureXCTest(name: "DependencyResolution/External/Complex") { fixturePath in - await XCTAssertBuilds( + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func canBuildMoreThanTwiceWithExternalDependencies( + data: BuildData + ) async throws { + try await fixture(name: "DependencyResolution/External/Complex") { fixturePath in + try await executeSwiftBuild( fixturePath.appending("app"), - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - await XCTAssertBuilds( + try await executeSwiftBuild( fixturePath.appending("app"), - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - await XCTAssertBuilds( + try await executeSwiftBuild( fixturePath.appending("app"), - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) } } - func testNoArgumentsExitsWithOne() async throws { - await XCTAssertThrowsCommandExecutionError( + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func noArgumentsExitsWithOne( + data: BuildData, + ) async throws { + await expectThrowsCommandExecutionError( try await executeSwiftBuild( "/", - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) ) { error in // if our code crashes we'll get an exit code of 256 guard error.result.exitStatus == .terminated(code: 1) else { - return XCTFail("failed in an unexpected manner: \(error)") + Issue.record("failed in an unexpected manner: \(error)") + return } } } - func testCompileFailureExitsGracefully() async throws { - try await fixtureXCTest(name: "Miscellaneous/CompileFails") { fixturePath in - await XCTAssertThrowsCommandExecutionError( + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func compileFailureExitsGracefully( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/CompileFails") { fixturePath in + await expectThrowsCommandExecutionError( try await executeSwiftBuild( fixturePath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) ) { error in // if our code crashes we'll get an exit code of 256 guard error.result.exitStatus == .terminated(code: 1) else { - return XCTFail("failed in an unexpected manner: \(error)") + Issue.record("failed in an unexpected manner: \(error)") + return + } + if data.buildSystem == .native { + #expect((error.stdout + error.stderr).contains("Compiling CompileFails Foo.swift")) + #expect((error.stdout + error.stderr).contains("compile_failure")) } - XCTAssertMatch(error.stdout + error.stderr, .contains("Compiling CompileFails Foo.swift")) - XCTAssertMatch(error.stdout + error.stderr, .regex(".*compile_failure.*")) - XCTAssertMatch(error.stdout + error.stderr, .regex(".*error:.*")) + #expect((error.stdout + error.stderr).contains("error:")) } } } - func testPackageManagerDefineAndXArgs() async throws { - try await fixtureXCTest(name: "Miscellaneous/-DSWIFT_PACKAGE") { fixturePath in - await XCTAssertBuildFails( - fixturePath, - buildSystem: .native, - ) - await XCTAssertBuilds( - fixturePath, - Xcc: ["-DEXTRA_C_DEFINE=2"], - Xswiftc: ["-DEXTRA_SWIFTC_DEFINE"], - buildSystem: .native, - ) + @Test( + // TODO: raise a GitHub issue on + // swift run swift-build --package-path Fixtures/Miscellaneous/-DSWIFT_PACKAGE --configuration release -Xcc -DEXTRA_C_DEFINE=2 -Xswiftc -DEXTRA_SWIFTC_DEFINE --build-system swiftbuild + // swift run swift-build --package-path Fixtures/Miscellaneous/-DSWIFT_PACKAGE --configuration debug -Xcc -DEXTRA_C_DEFINE=2 -Xswiftc -DEXTRA_SWIFTC_DEFINE --build-system swiftbuild + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func packageManagerDefineAndXArgs( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/-DSWIFT_PACKAGE") { fixturePath in + await #expect(throws: SwiftPMError.self) { + try await executeSwiftBuild( + fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, + ) + } + try await withKnownIssue { + try await executeSwiftBuild( + fixturePath, + configuration: data.config, + Xcc: ["-DEXTRA_C_DEFINE=2"], + Xswiftc: ["-DEXTRA_SWIFTC_DEFINE"], + buildSystem: data.buildSystem, + ) + } when: { + data.buildSystem == .swiftbuild + } } } /** Tests that modules that are rebuilt causes any executables that link to that module to be relinked. - */ - func testInternalDependencyEdges() async throws { - try await fixtureXCTest(name: "Miscellaneous/DependencyEdges/Internal") { fixturePath in - let execpath = fixturePath.appending(components: ".build", try UserToolchain.default.targetTriple.platformBuildPathComponent, "debug", "Foo").pathString + */ + @Test( + .IssueSwiftBuildLinuxRunnable, + .tags( + .Feature.Command.Build, + ), + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func internalDependencyEdges( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/DependencyEdges/Internal") { fixturePath in + let binPath = try fixturePath.appending(components: data.buildSystem.binPath(for: data.config)) + let executable = binPath.appending(components: "Foo") + let execPath = executable.pathString - await XCTAssertBuilds( + try await executeSwiftBuild( fixturePath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - var output = try await AsyncProcess.checkNonZeroExit(args: execpath) - XCTAssertEqual(output, "Hello\(ProcessInfo.EOL)") + try #require(localFileSystem.exists(executable), "File \(executable) does not exist") + try await withKnownIssue { + let output = try await AsyncProcess.checkNonZeroExit(args: execPath) + #expect(output == "Hello\(ProcessInfo.EOL)") + } when: { + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux + } // we need to sleep at least one second otherwise // llbuild does not realize the file has changed @@ -152,30 +244,53 @@ final class MiscellaneousTestCase: XCTestCase { try localFileSystem.writeFileContents(fixturePath.appending(components: "Bar", "Bar.swift"), bytes: "public let bar = \"Goodbye\"\n") - await XCTAssertBuilds( + try await executeSwiftBuild( fixturePath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - output = try await AsyncProcess.checkNonZeroExit(args: execpath) - XCTAssertEqual(output, "Goodbye\(ProcessInfo.EOL)") + try #require(localFileSystem.exists(executable), "File \(executable) does not exist") + try await withKnownIssue { + let output = try await AsyncProcess.checkNonZeroExit(args: execPath) + #expect(output == "Goodbye\(ProcessInfo.EOL)") + } when: { + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux + } } } /** Tests that modules from other packages that are rebuilt causes any executables that link to that module in the root package. - */ - func testExternalDependencyEdges1() async throws { - try await fixtureXCTest(name: "DependencyResolution/External/Complex") { fixturePath in - let execpath = fixturePath.appending(components: "app", ".build", try UserToolchain.default.targetTriple.platformBuildPathComponent, "debug", "Dealer").pathString + */ + @Test( + .IssueSwiftBuildLinuxRunnable, + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func externalDependencyEdges1( + data: BuildData + ) async throws { + try await fixture(name: "DependencyResolution/External/Complex") { fixturePath in + let packageRoot = fixturePath.appending(component: "app") + let binPath = try packageRoot.appending(components: data.buildSystem.binPath(for: data.config)) + let executable = binPath.appending(component: "Dealer") + let execPath = executable.pathString - let packageRoot = fixturePath.appending("app") - await XCTAssertBuilds( + try await executeSwiftBuild( packageRoot, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - var output = try await AsyncProcess.checkNonZeroExit(args: execpath).withSwiftLineEnding - XCTAssertEqual(output, "♣︎K\n♣︎Q\n♣︎J\n♣︎10\n♣︎9\n♣︎8\n♣︎7\n♣︎6\n♣︎5\n♣︎4\n") + try #require(localFileSystem.exists(executable), "File \(executable) does not exist.") + try await withKnownIssue { + let output = try await AsyncProcess.checkNonZeroExit(args: execPath).withSwiftLineEnding + #expect(output == "♣︎K\n♣︎Q\n♣︎J\n♣︎10\n♣︎9\n♣︎8\n♣︎7\n♣︎6\n♣︎5\n♣︎4\n") + } when: { + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux + } // we need to sleep at least one second otherwise // llbuild does not realize the file has changed @@ -185,12 +300,18 @@ final class MiscellaneousTestCase: XCTestCase { try localFileSystem.chmod(.userWritable, path: path, options: [.recursive]) try localFileSystem.writeFileContents(path.appending(components: "src", "Fisher-Yates_Shuffle.swift"), bytes: "public extension Collection{ func shuffle() -> [Iterator.Element] {return []} }\n\npublic extension MutableCollection where Index == Int { mutating func shuffleInPlace() { for (i, _) in enumerated() { self[i] = self[0] } }}\n\npublic let shuffle = true") - await XCTAssertBuilds( - fixturePath.appending("app"), - buildSystem: .native, + try await executeSwiftBuild( + packageRoot, + configuration: data.config, + buildSystem: data.buildSystem, ) - output = try await AsyncProcess.checkNonZeroExit(args: execpath).withSwiftLineEnding - XCTAssertEqual(output, "♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n") + try #require(localFileSystem.exists(executable), "File \(executable) does not exist.") + try await withKnownIssue { + let output = try await AsyncProcess.checkNonZeroExit(args: execPath).withSwiftLineEnding + #expect(output == "♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n♠︎A\n") + } when: { + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux + } } } @@ -198,17 +319,35 @@ final class MiscellaneousTestCase: XCTestCase { Tests that modules from other packages that are rebuilt causes any executables for another external package to be rebuilt. */ - func testExternalDependencyEdges2() async throws { - try await fixtureXCTest(name: "Miscellaneous/DependencyEdges/External") { fixturePath in - let execpath = [fixturePath.appending(components: "root", ".build", try UserToolchain.default.targetTriple.platformBuildPathComponent, "debug", "dep2").pathString] - + @Test( + .IssueSwiftBuildLinuxRunnable, + .tags( + .Feature.Command.Build, + ), + // arguments: getBuildData(for: [.swiftbuild]), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func externalDependencyEdges2( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/DependencyEdges/External") { fixturePath in let packageRoot = fixturePath.appending("root") - await XCTAssertBuilds( - fixturePath.appending("root"), - buildSystem: .native, + let binPath = try packageRoot.appending(components: data.buildSystem.binPath(for: data.config)) + let executable = binPath.appending(component: "dep2") + let execpath = [executable.pathString] + + try await executeSwiftBuild( + packageRoot, + configuration: data.config, + buildSystem: data.buildSystem, ) - var output = try await AsyncProcess.checkNonZeroExit(arguments: execpath) - XCTAssertEqual(output, "Hello\(ProcessInfo.EOL)") + try requireFileExists(at: executable) + try await withKnownIssue { + let output = try await AsyncProcess.checkNonZeroExit(arguments: execpath) + #expect(output == "Hello\(ProcessInfo.EOL)") + } when: { + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux + } // we need to sleep at least one second otherwise // llbuild does not realize the file has changed @@ -218,61 +357,119 @@ final class MiscellaneousTestCase: XCTestCase { try localFileSystem.chmod(.userWritable, path: path, options: [.recursive]) try localFileSystem.writeFileContents(path.appending(components: "Foo.swift"), bytes: "public let foo = \"Goodbye\"") - await XCTAssertBuilds( - fixturePath.appending("root"), - buildSystem: .native, + try await executeSwiftBuild( + packageRoot, + configuration: data.config, + buildSystem: data.buildSystem, ) - output = try await AsyncProcess.checkNonZeroExit(arguments: execpath) - XCTAssertEqual(output, "Goodbye\(ProcessInfo.EOL)") + try #require(localFileSystem.exists(executable), "File \"\(execpath)\" does not exists") + try await withKnownIssue { + let output = try await AsyncProcess.checkNonZeroExit(arguments: execpath) + #expect(output == "Goodbye\(ProcessInfo.EOL)") + } when: { + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux + } } } - func testSpaces() async throws { - try await fixtureXCTest(name: "Miscellaneous/Spaces Fixture") { fixturePath in - await XCTAssertBuilds( - fixturePath, - buildSystem: .native, - ) - XCTAssertFileExists(fixturePath.appending(components: ".build", try UserToolchain.default.targetTriple.platformBuildPathComponent, "debug", "Module_Name_1.build", "Foo.swift.o")) + @Test( + .IssueSwiftBuildSpaceInPath, + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func spaces( + data: BuildData, + ) async throws { + try await withKnownIssue(isIntermittent: true) { // might no longer be withKnownIssue, but leaving for now. + try await fixture(name: "Miscellaneous/Spaces Fixture") { fixturePath in + await #expect(throws: Never.self) { + try await executeSwiftBuild( + fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, + ) + } + let binPath = try fixturePath.appending(components: data.buildSystem.binPath(for: data.config)) + if data.buildSystem == .native { + expectFileExists(at: binPath.appending(components: "Module_Name_1.build", "Foo.swift.o")) + } + } + } when: { + data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux } } - func testSecondBuildIsNullInModulemapGen() throws { - // This has been failing on the Swift CI sometimes, need to investigate. - #if false + @Test( + .disabled("XCTest was disabled"), + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func secondBuildIsNullInModulemapGen( + data: BuildData, + ) async throws { // Make sure that swiftpm doesn't rebuild second time if the modulemap is being generated. - try fixtureXCTest(name: "CFamilyTargets/SwiftCMixed") { fixturePath in - var output = try await executeSwiftBuild( - prefix, - buildSystem: .native, - ) - XCTAssertFalse(output.isEmpty, output) - output = try await executeSwiftBuild( - prefix, - buildSystem: .native, - ) - XCTAssertTrue(output.isEmpty, output) + try await fixture(name: "CFamilyTargets/SwiftCMixed") { fixturePath in + let output = try await executeSwiftBuild( + fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, + ).stdout + #expect(!output.isEmpty) + let secondOutput = try await executeSwiftBuild( + fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, + ).stdout + #expect(secondOutput.isEmpty) } - #endif } - func testOverridingDeploymentTargetUsingSwiftCompilerArgument() async throws { - #if !os(macOS) - try XCTSkipIf(true, "test is only supported on macOS") - #endif - try await fixtureXCTest(name: "Miscellaneous/DistantFutureDeploymentTarget") { fixturePath in - let hostTriple = try UserToolchain.default.targetTriple - try await executeSwiftBuild( - fixturePath, - Xswiftc: ["-target", "\(hostTriple.archName)-apple-macosx41.0"], - buildSystem: .native, - ) + @Test( + // TODO: Raise a GitHub issue as Swift Build fails + //. swift run swift-build --package-path Fixtures/Miscellaneous/DistantFutureDeploymentTarget --configuration release -Xswiftc -target -Xswiftc arm64-apple-macosx41.0 --build-system swiftbuild + //. swift run swift-build --package-path Fixtures/Miscellaneous/DistantFutureDeploymentTarget --configuration debug -Xswiftc -target -Xswiftc arm64-apple-macosx41.0 --build-system swiftbuild + .requireHostOS(.macOS), + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func overridingDeploymentTargetUsingSwiftCompilerArgument( + data: BuildData, + ) async throws { + try await withKnownIssue { + try await fixture(name: "Miscellaneous/DistantFutureDeploymentTarget") { fixturePath in + let hostTriple = try UserToolchain.default.targetTriple + try await executeSwiftBuild( + fixturePath, + configuration: data.config, + Xswiftc: ["-target", "\(hostTriple.archName)-apple-macosx41.0"], + buildSystem: data.buildSystem, + ) + } + } when: { + data.buildSystem == .swiftbuild } } - func testPkgConfigCFamilyTargets() async throws { - try XCTSkipOnWindows(because: "fails to build on windows (maybe not be supported?)") - try await fixtureXCTest(name: "Miscellaneous/PkgConfig") { fixturePath in + @Test( + .serialized, // Because the tests set the environment variable + // TODO: raise a GitHub issue . the second `swift-build` command fails. + .tags( + .Feature.Command.Build, + ), + .requires(executable: executableName("clang")), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func pkgConfigCFamilyTargets( + data: BuildData, + ) async throws { + // try XCTSkipOnWindows(because: "fails to build on windows (maybe not be supported?)") + try await fixture(name: "Miscellaneous/PkgConfig") { fixturePath in let systemModule = fixturePath.appending("SystemModule") // Create a shared library. let input = systemModule.appending(components: "Sources", "SystemModule.c") @@ -299,36 +496,56 @@ final class MiscellaneousTestCase: XCTestCase { let moduleUser = fixturePath.appending("SystemModuleUserClang") let env: Environment = ["PKG_CONFIG_PATH": fixturePath.pathString] - _ = try await executeSwiftBuild( - moduleUser, - env: env, - buildSystem: .native, - ) - - XCTAssertFileExists(moduleUser.appending(components: ".build", triple.platformBuildPathComponent, "debug", "SystemModuleUserClang")) + let binPath = try moduleUser.appending(components: data.buildSystem.binPath(for: data.config)) + await withKnownIssue { + await #expect(throws: Never.self) { + _ = try await executeSwiftBuild( + moduleUser, + configuration: data.config, + env: env, + buildSystem: data.buildSystem, + ) + } + expectFileExists(at: binPath.appending(component: "SystemModuleUserClang")) + } when:{ + data.buildSystem == .swiftbuild && data.config == .release && ProcessInfo.hostOperatingSystem != .linux + } // Clean up the build directory before re-running the build with // different arguments. _ = try await executeSwiftPackage( moduleUser, + configuration: data.config, extraArgs: ["clean"], - buildSystem: .native, + buildSystem: data.buildSystem, ) - _ = try await executeSwiftBuild( - moduleUser, - extraArgs: ["--pkg-config-path", fixturePath.pathString], - buildSystem: .native, - ) + await withKnownIssue { + await #expect(throws: Never.self) { + _ = try await executeSwiftBuild( + moduleUser, + configuration: data.config, + extraArgs: ["--pkg-config-path", fixturePath.pathString], + buildSystem: data.buildSystem, + ) + } - XCTAssertFileExists(moduleUser.appending(components: ".build", triple.platformBuildPathComponent, "debug", "SystemModuleUserClang")) + expectFileExists(at: binPath.appending(component: "SystemModuleUserClang")) + } when: { + data.buildSystem == .swiftbuild + } } } - func testCanKillSubprocessOnSigInt() throws { - // swift-pm: Spurious? failures of MiscellaneousTestCase.testCanKillSubprocessOnSigInt on linux - #if false - try fixtureXCTest(name: "DependencyResolution/External/Simple") { fixturePath in + @Test( + .disabled("XCTest was disabled"), + .tags( + .Feature.Command.Build, + ), + ) + func canKillSubprocessOnSigInt() throws { +#if false + try fixture(name: "DependencyResolution/External/Simple") { fixturePath in let fakeGit = fixturePath.appending(components: "bin", "git") let waitFile = fixturePath.appending(components: "waitfile") @@ -362,68 +579,91 @@ final class MiscellaneousTestCase: XCTestCase { try process.launch() guard waitForFile(waitFile) else { - return XCTFail("Couldn't launch the process") + Issue.record("Couldn't launch the process") + return } // Interrupt the process. process.signal(SIGINT) let result = try process.waitUntilExit() // We should not have exited with zero. - XCTAssert(result.exitStatus != .terminated(code: 0)) + #expect(result.exitStatus != .terminated(code: 0)) // Process and subprocesses should be dead. let contents: String = try localFileSystem.readFileContents(waitFile) - XCTAssertFalse(try AsyncProcess.running(process.processID)) - XCTAssertFalse(try AsyncProcess.running(ProcessID(contents)!)) + try #expect(!AsyncProcess.running(process.processID)) + try #expect(!AsyncProcess.running(ProcessID(contents)!)) } - #endif +#endif } - func testReportingErrorFromGitCommand() async throws { - try await fixtureXCTest(name: "Miscellaneous/MissingDependency") { fixturePath in + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func reportingErrorFromGitCommand( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/MissingDependency") { fixturePath in // This fixture has a setup that is intentionally missing a local // dependency to induce a failure. // Launch swift-build. let app = fixturePath.appending("Bar") - await XCTAssertAsyncThrowsError( + await expectThrowsCommandExecutionError( try await executeSwiftBuild( app, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) ) { error in // We should exited with a failure from the attempt to "git clone" // something that doesn't exist. - guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { - return XCTFail("invalid error \(error)") - } - XCTAssert(stderr.contains("error: Failed to clone repository"), "Error from git was not propagated to process output: \(stderr)") + let stderr = error.stderr + #expect( + stderr.contains("error: Failed to clone repository"), + "Error from git was not propagated to process output: \(stderr)", + ) } } } - func testLocalPackageUsedAsURLValidation() async throws { - try await fixtureXCTest(name: "Miscellaneous/LocalPackageAsURL", createGitRepo: false) { fixturePath in + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func localPackageUsedAsURLValidation( + data: BuildData + ) async throws { + try await fixture(name: "Miscellaneous/LocalPackageAsURL", createGitRepo: false) { fixturePath in // This fixture has a setup that is trying to use a local package // as a url that hasn't been initialized as a repo - await XCTAssertAsyncThrowsError( + await expectThrowsCommandExecutionError( try await executeSwiftBuild( fixturePath.appending("Bar"), - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) ) { error in - guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { - return XCTFail("invalid error \(error)") - } - XCTAssert(stderr.contains("cannot clone from local directory"), "Didn't find expected output: \(stderr)") + let stderr = error.stderr + #expect( + stderr.contains("cannot clone from local directory"), + "Didn't find expected output: \(stderr)", + ) } } } - func testLTO() async throws { - throw XCTSkip("No longer works with newer toolchains") - #if os(macOS) + @Test( + .disabled("No longer works with newer toolchains"), + ) + func lTO() async throws { +#if os(macOS) // FIXME: this test requires swift-driver to be installed // Currently swift-ci does not build/install swift-driver before running // swift-package-manager tests which results in this test failing. @@ -431,167 +671,239 @@ final class MiscellaneousTestCase: XCTestCase { // - https://github.com/swiftlang/swift/pull/69696 // - https://github.com/swiftlang/swift/pull/61766 // - https://github.com/swiftlang/swift-package-manager/pull/5842#issuecomment-1301632685 - try await fixtureXCTest(name: "Miscellaneous/LTO/SwiftAndCTargets") { fixturePath in + try await fixture(name: "Miscellaneous/LTO/SwiftAndCTargets") { fixturePath in /*let output =*/ try await executeSwiftBuild( fixturePath, extraArgs: ["--experimental-lto-mode=full", "--verbose"], buildSystem: .native, - + ) // FIXME: On macOS dsymutil cannot find temporary .o files? (#6890) // Ensure warnings like the following are not present in build output // warning: (arm64) /var/folders/ym/6l_0x8vj0b70sz_4h9d70p440000gn/T/main-e120de.o unable to open object file: No such file or directory // XCTAssertNoMatch(output.stdout, .contains("unable to open object file")) } - #endif +#endif } - func testUnicode() async throws { - try XCTSkipOnWindows(because: "Filepath too long error") - #if !os(Linux) && !os(Android) // TODO: - Linux has trouble with this and needs investigation. - try await fixtureXCTest(name: "Miscellaneous/Unicode") { fixturePath in - // See the fixture manifest for an explanation of this string. - let complicatedString = "πשּׁµ𝄞🇺🇳🇮🇱x̱̱̱̱̱̄̄̄̄̄" - let verify = "\u{03C0}\u{0FB2C}\u{00B5}\u{1D11E}\u{1F1FA}\u{1F1F3}\u{1F1EE}\u{1F1F1}\u{0078}\u{0331}\u{0304}\u{0331}\u{0304}\u{0331}\u{0304}\u{0331}\u{0304}\u{0331}\u{0304}" - XCTAssert( - complicatedString.unicodeScalars.elementsEqual(verify.unicodeScalars), - "\(complicatedString) ≠ \(verify)") - - // ••••• Set up dependency. - let dependencyName = "UnicodeDependency‐\(complicatedString)" - let dependencyOrigin = AbsolutePath(#file).parentDirectory.parentDirectory.parentDirectory - .appending("Fixtures") - .appending("Miscellaneous") - .appending(component: dependencyName) - let dependencyDestination = fixturePath.parentDirectory.appending(component: dependencyName) - try? FileManager.default.removeItem(atPath: dependencyDestination.pathString) - defer { try? FileManager.default.removeItem(atPath: dependencyDestination.pathString) } - try FileManager.default.copyItem( - atPath: dependencyOrigin.pathString, - toPath: dependencyDestination.pathString) - let dependency = GitRepository(path: dependencyDestination) - try dependency.create() - try dependency.stageEverything() - try dependency.commit() - try dependency.tag(name: "1.0.0") - // ••••• - - // Attempt several operations. - try await executeSwiftTest( - fixturePath, - buildSystem: .native, - ) - try await executeSwiftRun( - fixturePath, - complicatedString + "‐tool", - buildSystem: .native, - ) + @Test( + .IssueWindowsLongPath, + .IssueSwiftBuildLinuxRunnable, + .skipHostOS(.linux), + .skipHostOS(.android), + .tags( + .Feature.Command.Test, + .Feature.Command.Run, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func unicode( + data: BuildData, + ) async throws { + try await withKnownIssue { + try await fixture(name: "Miscellaneous/Unicode") { fixturePath in + // See the fixture manifest for an explanation of this string. + let complicatedString = "πשּׁµ𝄞🇺🇳🇮🇱x̱̱̱̱̱̄̄̄̄̄" + let verify = "\u{03C0}\u{0FB2C}\u{00B5}\u{1D11E}\u{1F1FA}\u{1F1F3}\u{1F1EE}\u{1F1F1}\u{0078}\u{0331}\u{0304}\u{0331}\u{0304}\u{0331}\u{0304}\u{0331}\u{0304}\u{0331}\u{0304}" + #expect( + complicatedString.unicodeScalars.elementsEqual(verify.unicodeScalars), + "\(complicatedString) ≠ \(verify)", + ) + + // ••••• Set up dependency. + let dependencyName = "UnicodeDependency‐\(complicatedString)" + let dependencyOrigin = AbsolutePath(#file).parentDirectory.parentDirectory.parentDirectory + .appending("Fixtures") + .appending("Miscellaneous") + .appending(component: dependencyName) + let dependencyDestination = fixturePath.parentDirectory.appending(component: dependencyName) + try? FileManager.default.removeItem(atPath: dependencyDestination.pathString) + defer { try? FileManager.default.removeItem(atPath: dependencyDestination.pathString) } + try FileManager.default.copyItem( + atPath: dependencyOrigin.pathString, + toPath: dependencyDestination.pathString) + let dependency = GitRepository(path: dependencyDestination) + try dependency.create() + try dependency.stageEverything() + try dependency.commit() + try dependency.tag(name: "1.0.0") + // ••••• + + // Attempt several operations. + try await executeSwiftTest( + fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, + ) + try await withKnownIssue { + try await executeSwiftRun( + fixturePath, + complicatedString + "‐tool", + configuration: data.config, + buildSystem: data.buildSystem, + ) + } when: { + ProcessInfo.hostOperatingSystem == .linux && data.buildSystem == .swiftbuild + } + } + } when: { + ProcessInfo.hostOperatingSystem == .windows } - #endif } - func testTestsCanLinkAgainstExecutable() async throws { - try await fixtureXCTest(name: "Miscellaneous/TestableExe") { fixturePath in + @Test( + .skipHostOS(.linux), // TODO: raise GitHub issue.. crashed on Linux + .tags( + .Feature.Command.Test, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func testsCanLinkAgainstExecutable( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/TestableExe") { fixturePath in do { let (stdout, stderr) = try await executeSwiftTest( fixturePath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) // in "swift test" build output goes to stderr - XCTAssertMatch(stderr, .contains("Linking TestableExe1")) - XCTAssertMatch(stderr, .contains("Linking TestableExe2")) - XCTAssertMatch(stderr, .contains("Linking TestableExePackageTests")) - XCTAssertMatch(stderr, .contains("Build complete!")) + if data.buildSystem == .native { + #expect(stderr.contains("Linking TestableExe1")) + #expect(stderr.contains("Linking TestableExe2")) + #expect(stderr.contains("Linking TestableExePackageTests")) + } + #expect(stderr.contains("Build complete!")) // in "swift test" test output goes to stdout - XCTAssertMatch(stdout, .contains("Executed 1 test")) - XCTAssertMatch(stdout, .contains("Hello, world")) - XCTAssertMatch(stdout, .contains("Hello, planet")) + #expect(stdout.contains("Executed 1 test")) + #expect(stdout.contains("Hello, world")) + #expect(stdout.contains("Hello, planet")) } catch { - #if os(macOS) && arch(arm64) +#if os(macOS) && arch(arm64) // Add some logging but ignore the failure for an environment being investigated. let (stdout, stderr) = try await executeSwiftTest( fixturePath, + configuration: data.config, extraArgs: ["-v"], - buildSystem: .native, + buildSystem: data.buildSystem, ) - print("testTestsCanLinkAgainstExecutable failed") + print("\(String(describing: Test.current?.name)) failed") print("ENV:\n") for (k, v) in Environment.current.sorted(by: { $0.key < $1.key }) { print(" \(k)=\(v)") } print("STDOUT:\n\(stdout)") print("STDERR:\n\(stderr)") - #else - XCTFail("\(error)") - #endif +#else + Issue.record("\(error)") +#endif } } } + @Test( + .tags( + .Feature.Command.Test, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) @available(macOS 15, *) - func testTestsCanLinkAgainstAsyncExecutable() async throws { - try await fixtureXCTest(name: "Miscellaneous/TestableAsyncExe") { fixturePath in + func testsCanLinkAgainstAsyncExecutable( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/TestableAsyncExe") { fixturePath in let (stdout, stderr) = try await executeSwiftTest( fixturePath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) // in "swift test" build output goes to stderr - XCTAssertMatch(stderr, .contains("Linking TestableAsyncExe1")) - XCTAssertMatch(stderr, .contains("Linking TestableAsyncExe2")) - XCTAssertMatch(stderr, .contains("Linking TestableAsyncExe3")) - XCTAssertMatch(stderr, .contains("Linking TestableAsyncExe4")) - XCTAssertMatch(stderr, .contains("Linking TestableAsyncExePackageTests")) - XCTAssertMatch(stderr, .contains("Build complete!")) + if data.buildSystem == .native { + #expect(stderr.contains("Linking TestableAsyncExe1")) + #expect(stderr.contains("Linking TestableAsyncExe2")) + #expect(stderr.contains("Linking TestableAsyncExe3")) + #expect(stderr.contains("Linking TestableAsyncExe4")) + #expect(stderr.contains("Linking TestableAsyncExePackageTests")) + } + #expect(stderr.contains("Build complete!")) // in "swift test" test output goes to stdout - XCTAssertMatch(stdout, .contains("Executed 1 test")) - XCTAssertMatch(stdout, .contains("Hello, async world")) - XCTAssertMatch(stdout, .contains("Hello, async planet")) - XCTAssertMatch(stdout, .contains("Hello, async galaxy")) - XCTAssertMatch(stdout, .contains("Hello, async universe")) + #expect(stdout.contains("Executed 1 test")) + #expect(stdout.contains("Hello, async world")) + #expect(stdout.contains("Hello, async planet")) + #expect(stdout.contains("Hello, async galaxy")) + #expect(stdout.contains("Hello, async universe")) } } - func testExecutableTargetMismatch() async throws { - try await fixtureXCTest(name: "Miscellaneous/TargetMismatch") { path in - do { - let output = try await executeSwiftBuild( - path, - buildSystem: .native, - ) - // in "swift build" build output goes to stdout - XCTAssertMatch(output.stdout, .contains("Compiling Sample main.swift")) - XCTAssertMatch(output.stderr, .contains("The target named 'Sample' was identified as an executable target but a non-executable product with this name already exists.")) - } catch { - XCTFail("\(error)") + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func executableTargetMismatch( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/TargetMismatch") { path in + let (stdout, stderr) = try await executeSwiftBuild( + path, + configuration: data.config, + buildSystem: data.buildSystem, + ) + // in "swift build" build output goes to stdout + if data.buildSystem == .native { + #expect(stdout.contains("Compiling Sample main.swift")) } + #expect(stderr.contains("The target named 'Sample' was identified as an executable target but a non-executable product with this name already exists.")) } } - func testLibraryTriesToIncludeExecutableTarget() async throws { - try await fixtureXCTest(name: "Miscellaneous/PackageWithMalformedLibraryProduct") { path in - await XCTAssertThrowsCommandExecutionError( + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func libraryTriesToIncludeExecutableTarget( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/PackageWithMalformedLibraryProduct") { path in + await expectThrowsCommandExecutionError( try await executeSwiftBuild( path, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) ) { error in // if our code crashes we'll get an exit code of 256 guard error.result.exitStatus == .terminated(code: 1) else { - return XCTFail("failed in an unexpected manner: \(error)") + Issue.record("failed in an unexpected manner: \(error)") + return } - XCTAssertMatch(error.stdout + error.stderr, .contains("library product 'PackageWithMalformedLibraryProduct' should not contain executable targets (it has 'PackageWithMalformedLibraryProduct')")) + #expect((error.stdout + error.stderr).contains("library product 'PackageWithMalformedLibraryProduct' should not contain executable targets (it has 'PackageWithMalformedLibraryProduct')")) } } } - func testEditModeEndToEnd() async throws { - try await fixtureXCTest(name: "Miscellaneous/Edit") { fixturePath in - #if os(Windows) + @Test( + .tags( + .Feature.Command.Build, + .Feature.Command.Package.Edit, + .Feature.Command.Package.Unedit, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func editModeEndToEnd( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/Edit") { fixturePath in +#if os(Windows) let prefix = fixturePath - #else +#else let prefix = try resolveSymlinks(fixturePath) - #endif +#endif let appPath = fixturePath.appending("App") // prepare the dependencies as git repos @@ -604,283 +916,312 @@ final class MiscellaneousTestCase: XCTestCase { // make sure it builds let output = try await executeSwiftBuild( appPath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) // package resolution output goes to stderr - XCTAssertMatch(output.stderr, .contains("Fetching \(prefix.appending("Foo").pathString)")) - XCTAssertMatch(output.stderr, .contains("Fetched \(prefix.appending("Foo").pathString)")) - XCTAssertMatch(output.stderr, .contains("Creating working copy for \(prefix.appending("Foo").pathString)")) - XCTAssertMatch(output.stderr, .contains("Fetching \(prefix.appending("Bar").pathString)")) - XCTAssertMatch(output.stderr, .contains("Fetched \(prefix.appending("Bar").pathString)")) - XCTAssertMatch(output.stderr, .contains("Creating working copy for \(prefix.appending("Bar").pathString)")) + #expect(output.stderr.contains("Fetching \(prefix.appending("Foo").pathString)")) + #expect(output.stderr.contains("Fetched \(prefix.appending("Foo").pathString)")) + #expect(output.stderr.contains("Creating working copy for \(prefix.appending("Foo").pathString)")) + #expect(output.stderr.contains("Fetching \(prefix.appending("Bar").pathString)")) + #expect(output.stderr.contains("Fetched \(prefix.appending("Bar").pathString)")) + #expect(output.stderr.contains("Creating working copy for \(prefix.appending("Bar").pathString)")) // in "swift build" build output goes to stdout - XCTAssertMatch(output.stdout, .contains("Build complete!")) + #expect(output.stdout.contains("Build complete!")) } // put foo into edit mode _ = try await executeSwiftPackage( appPath, + configuration: data.config, extraArgs: ["edit", "Foo"], - buildSystem: .native, + buildSystem: data.buildSystem, ) - XCTAssertDirectoryExists(appPath.appending(components: ["Packages", "Foo"])) + expectDirectoryExists(at: appPath.appending(components: ["Packages", "Foo"])) do { // build again in edit mode let output = try await executeSwiftBuild( appPath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - XCTAssertMatch(output.stdout, .contains("Build complete!")) + #expect(output.stdout.contains("Build complete!")) } do { // take foo out of edit mode let output = try await executeSwiftPackage( appPath, + configuration: data.config, extraArgs: ["unedit", "Foo"], - buildSystem: .native, + buildSystem: data.buildSystem, ) // package resolution output goes to stderr - XCTAssertMatch(output.stderr, .contains("Creating working copy for \(prefix.appending("Foo"))")) - XCTAssertNoSuchPath(appPath.appending(components: ["Packages", "Foo"])) + #expect(output.stderr.contains("Creating working copy for \(prefix.appending("Foo"))")) + expectFileDoesNotExists(at: appPath.appending(components: ["Packages", "Foo"])) } // build again in edit mode do { let output = try await executeSwiftBuild( appPath, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) // in "swift build" build output goes to stdout - XCTAssertMatch(output.stdout, .contains("Build complete!")) + #expect(output.stdout.contains("Build complete!")) } } } - func testCustomCachePath() async throws { - try await fixtureXCTest(name: "Miscellaneous/Simple") { path in - let customCachePath = path.appending(components: "custom", "cache") - XCTAssertNoSuchPath(customCachePath) + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms),[ "cache-path", "config-path", "security-path"], + ) + func customCachePath( + data: BuildData, + pathOption: String, + ) async throws { + try await fixture(name: "Miscellaneous/Simple") { path in + let customPath = path.appending(components: "custom", pathOption) + expectFileDoesNotExists(at: customPath) + expectDirectoryDoesNotExist(at: customPath) try await executeSwiftBuild( path, - extraArgs: ["--cache-path", customCachePath.pathString], - buildSystem: .native, + configuration: data.config, + extraArgs: ["--\(pathOption)", customPath.pathString], + buildSystem: data.buildSystem, ) - XCTAssertDirectoryExists(customCachePath) + expectDirectoryExists(at: customPath) } + } - // `FileSystem` does not support `chmod` on Linux - #if os(macOS) - try await fixtureXCTest(name: "Miscellaneous/Simple") { path in + @Test( + // .requireHostOS(.macOS), /// we should test this on Linux + .skipHostOS(.linux), /// Skipping on Linux, but does chmod work on Windows? + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms),[ "cache-path", "config-path", "security-path"], + ) + func customPathUserUnwritableGeneratesPermissionError( + data: BuildData, + pathOption: String, + ) async throws { + try await fixture(name: "Miscellaneous/Simple") { path in + // `FileSystem` does not support `chmod` on Linux try localFileSystem.chmod(.userUnWritable, path: path) - let customCachePath = path.appending(components: "custom", "cache") - XCTAssertNoSuchPath(customCachePath) - await XCTAssertAsyncThrowsError( + defer { + do { + try localFileSystem.chmod(.userWritable, path: path) + } catch { + // Do nothing + } + } + let customPath = path.appending(components: "custom", pathOption) + expectFileDoesNotExists(at: customPath) + expectDirectoryDoesNotExist(at: customPath) + await expectThrowsCommandExecutionError( try await executeSwiftBuild( path, - extraArgs: ["--cache-path", customCachePath.pathString], - buildSystem: .native, + configuration: data.config, + extraArgs: ["--\(pathOption)", customPath.pathString], + buildSystem: data.buildSystem, ) ) { error in - guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { - return XCTFail("invalid error \(error)") - } - XCTAssert(stderr.contains("error: invalid access to "), "expected permissions error. stderr: '\(stderr)'") + let stderr = error.stderr + #expect(stderr.contains("error: invalid access to"), "expected permissions error. stderr: '\(stderr)', stdout '\(error.stdout)'") } - XCTAssertNoSuchPath(customCachePath) + expectFileDoesNotExists(at: customPath) + expectDirectoryDoesNotExist(at: customPath) } - #endif } - func testCustomConfigPath() async throws { - try await fixtureXCTest(name: "Miscellaneous/Simple") { path in - let customConfigPath = path.appending(components: "custom", "config") - XCTAssertNoSuchPath(customConfigPath) - try await executeSwiftBuild( - path, - extraArgs: ["--config-path", customConfigPath.pathString], - buildSystem: .native, - ) - XCTAssertDirectoryExists(customConfigPath) - } - // `FileSystem` does not support `chmod` on Linux - #if os(macOS) - try await fixtureXCTest(name: "Miscellaneous/Simple") { path in - try localFileSystem.chmod(.userUnWritable, path: path) - let customConfigPath = path.appending(components: "custom", "config") - XCTAssertNoSuchPath(customConfigPath) - await XCTAssertAsyncThrowsError( - try await executeSwiftBuild( + @Test( + .IssueSwiftBuildLinuxRunnable, + .IssueWindowsRelativePathAssert, + .IssueWindowsAbsoluteAndRelativePathTestFailures, + // TODO: raise github issue.. swift run fails to execute the executable + // Build complete! (4.65 secs.) + // PluginGeneratedResources/PluginGeneratedResources.swift:9: Fatal error: Unexpectedly found nil while unwrapping an Optional value + // [1] 13225 trace trap swift run swift-run --package-path --configuration debug --build-system + .requiresSwiftConcurrencySupport, + .tags( + .Feature.Command.Run, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms) + ) + func pluginGeneratedResources( + data: BuildData, + ) async throws { + try await withKnownIssue { + try await fixture(name: "Miscellaneous/PluginGeneratedResources") { path in + let result = try await executeSwiftRun( path, - extraArgs: ["--config-path", customConfigPath.pathString], - buildSystem: .native, + nil, + configuration: data.config, + buildSystem: data.buildSystem, ) - ) { error in - guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { - return XCTFail("invalid error \(error)") - } - XCTAssert(stderr.contains("error: invalid access to "), "expected permissions error. stderr: '\(stderr)'") + #expect(result.stdout == "Hello, World!\n") + #expect(result.stderr.contains("Copying best.txt\n"), "build log is missing message about copying resource file") } - XCTAssertNoSuchPath(customConfigPath) + }when: { + (data.buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux) + || (data.buildSystem == .swiftbuild) + || (ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline) } - #endif } - func testCustomSecurityPath() async throws { - try await fixtureXCTest(name: "Miscellaneous/Simple") { path in - let customSecurityPath = path.appending(components: "custom", "security") - XCTAssertNoSuchPath(customSecurityPath) - try await executeSwiftBuild( - path, - extraArgs: ["--security-path", customSecurityPath.pathString], - buildSystem: .native, - ) - XCTAssertDirectoryExists(customSecurityPath) - } - - // `FileSystem` does not support `chmod` on Linux - #if os(macOS) - try await fixtureXCTest(name: "Miscellaneous/Simple") { path in - try localFileSystem.chmod(.userUnWritable, path: path) - let customSecurityPath = path.appending(components: "custom", "security") - XCTAssertNoSuchPath(customSecurityPath) - await XCTAssertAsyncThrowsError( + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms) + ) + func compileCXX17CrashWithFModules( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/CXX17CompilerCrash/v5_8") { fixturePath in + await #expect(throws: Never.self) { try await executeSwiftBuild( - path, - extraArgs: ["--security-path", customSecurityPath.pathString], - buildSystem: .native, + fixturePath, + configuration: data.config, + buildSystem: data.buildSystem, ) - ) { error in - guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { - return XCTFail("invalid error \(error)") - } - XCTAssert(stderr.contains("error: invalid access to "), "expected permissions error. stderr: '\(stderr)'") } } - #endif } - func testPluginGeneratedResources() async throws { - // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). - try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") - try XCTSkipOnWindows( - because: """ - Invalid path. Possibly related to https://github.com/swiftlang/swift-package-manager/issues/8511 or https://github.com/swiftlang/swift-package-manager/issues/8602 - """, - skipPlatformCi: true, - ) - - try await fixtureXCTest(name: "Miscellaneous/PluginGeneratedResources") { path in - let result = try await executeSwiftRun( - path, - nil, - buildSystem: .native, - ) - XCTAssertEqual(result.stdout, "Hello, World!\n", "executable did not produce expected output") - XCTAssertTrue(result.stderr.contains("Copying best.txt\n"), "build log is missing message about copying resource file") - } - } - - func testCompileCXX17CrashWithFModules() async throws { - try await fixtureXCTest(name: "Miscellaneous/CXX17CompilerCrash/v5_8") { fixturePath in - await XCTAssertBuilds( - fixturePath, - buildSystem: .native, - ) - } - } - - func testNoJSONOutputWithFlatPackageStructure() async throws { - try await fixtureXCTest(name: "Miscellaneous/FlatPackage") { package in + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms) + ) + func noJSONOutputWithFlatPackageStructure( + data: BuildData, + ) async throws { + try await fixture(name: "Miscellaneous/FlatPackage") { package in // First build, make sure we got the `.build` directory where we expect it, and that there is no JSON output (by looking for known output). let (stdout1, stderr1) = try await executeSwiftBuild( package, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - XCTAssertDirectoryExists(package.appending(".build")) - XCTAssertNoMatch(stdout1, .contains("command_arguments")) - XCTAssertNoMatch(stderr1, .contains("command_arguments")) - + expectDirectoryExists(at: package.appending(".build")) + #expect(!stdout1.contains("command_arguments")) + #expect(!stderr1.contains("command_arguments")) + // Now test, make sure we got the `.build` directory where we expect it, and that there is no JSON output (by looking for known output). let (stdout2, stderr2) = try await executeSwiftTest( package, - buildSystem: .native, + configuration: data.config, + buildSystem: data.buildSystem, ) - XCTAssertDirectoryExists(package.appending(".build")) - XCTAssertNoMatch(stdout2, .contains("command_arguments")) - XCTAssertNoMatch(stderr2, .contains("command_arguments")) + expectDirectoryExists(at: package.appending(".build")) + #expect(!stdout2.contains("command_arguments")) + #expect(!stderr2.contains("command_arguments")) } } - func testNoWarningFromRemoteDependencies() async throws { - try await fixtureXCTest(name: "Miscellaneous/DependenciesWarnings") { path in - // prepare the deps as git sources - let dependency1Path = path.appending("dep1") - initGitRepo(dependency1Path, tag: "1.0.0") - let dependency2Path = path.appending("dep2") - initGitRepo(dependency2Path, tag: "1.0.0") - - let appPath = path.appending("app") - let (stdout, stderr) = try await executeSwiftBuild( - appPath, - buildSystem: .native, - ) - XCTAssertDirectoryExists(appPath.appending(".build")) - XCTAssertMatch(stdout + stderr, .contains("'DeprecatedApp' is deprecated")) - XCTAssertNoMatch(stdout + stderr, .contains("'Deprecated1' is deprecated")) - XCTAssertNoMatch(stdout + stderr, .contains("'Deprecated2' is deprecated")) - } - } - - func testNoWarningFromRemoteDependenciesWithWarningsAsErrors() async throws { - try await fixtureXCTest(name: "Miscellaneous/DependenciesWarnings2") { path in - // prepare the deps as git sources - let dependency1Path = path.appending("dep1") - initGitRepo(dependency1Path, tag: "1.0.0") - let dependency2Path = path.appending("dep2") - initGitRepo(dependency2Path, tag: "1.0.0") - - let appPath = path.appending("app") - let (stdout, stderr) = try await executeSwiftBuild( - appPath, - Xswiftc: ["-warnings-as-errors"], - buildSystem: .native, - ) - XCTAssertDirectoryExists(appPath.appending(".build")) - XCTAssertNoMatch(stdout + stderr, .contains("'Deprecated1' is deprecated")) - XCTAssertNoMatch(stdout + stderr, .contains("'Deprecated2' is deprecated")) + @Test( + .tags( + .Feature.Command.Build, + ), + .issue("https://github.com/swiftlang/swift-package-manager/issues/9078", relationship: .defect), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms) + ) + func noWarningFromRemoteDependencies( + data: BuildData, + ) async throws { + try await withKnownIssue { + try await fixture(name: "Miscellaneous/DependenciesWarnings") { path in + // prepare the deps as git sources + let dependency1Path = path.appending("dep1") + initGitRepo(dependency1Path, tag: "1.0.0") + let dependency2Path = path.appending("dep2") + initGitRepo(dependency2Path, tag: "1.0.0") + + let appPath = path.appending("app") + let (stdout, stderr) = try await executeSwiftBuild( + appPath, + configuration: data.config, + buildSystem: data.buildSystem, + ) + expectDirectoryExists(at: appPath.appending(".build")) + #expect((stdout + stderr).contains("'DeprecatedApp' is deprecated")) + #expect(!(stdout + stderr).contains("'Deprecated1' is deprecated")) + #expect(!(stdout + stderr).contains("'Deprecated2' is deprecated")) + } + } when: { + ProcessInfo.hostOperatingSystem == .linux && data.buildSystem == .swiftbuild } } - func testRootPackageWithConditionals() async throws { - try await fixtureXCTest(name: "Miscellaneous/RootPackageWithConditionals") { path in - let (_, stderr) = try await executeSwiftBuild( - path, - env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"], - buildSystem: .native, - ) - let errors = stderr.components(separatedBy: .newlines).filter { !$0.contains("[logging] misuse") && !$0.isEmpty } - .filter { !$0.contains("Unable to locate libSwiftScan") } - XCTAssertEqual(errors, [], "unexpected errors: \(errors)") + @Test( + //TODO: Raise issue against SwiftBuild + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms) + ) + func noWarningFromRemoteDependenciesWithWarningsAsErrors( + data: BuildData, + ) async throws { + try await withKnownIssue { + try await fixture(name: "Miscellaneous/DependenciesWarnings2") { path in + // prepare the deps as git sources + let dependency1Path = path.appending("dep1") + initGitRepo(dependency1Path, tag: "1.0.0") + let dependency2Path = path.appending("dep2") + initGitRepo(dependency2Path, tag: "1.0.0") + + let appPath = path.appending("app") + let (stdout, stderr) = try await executeSwiftBuild( + appPath, + configuration: data.config, + Xswiftc: ["-warnings-as-errors"], + buildSystem: data.buildSystem, + ) + expectDirectoryExists(at: appPath.appending(".build")) + #expect(!(stdout + stderr).contains("'Deprecated1' is deprecated")) + #expect(!(stdout + stderr).contains("'Deprecated2' is deprecated")) + } + } when: { + data.buildSystem == .swiftbuild } } - func testRootPackageWithConditionalsSwiftBuild() async throws { - try XCTSkipOnWindows(because: "produces a filename that is too long, needs investigation") -#if os(Linux) - if FileManager.default.contents(atPath: "/etc/system-release").map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false { - throw XCTSkip("Skipping Swift Build testing on Amazon Linux because of platform issues.") - } -#endif - try await fixtureXCTest(name: "Miscellaneous/RootPackageWithConditionals") { path in - _ = try await executeSwiftBuild( - path, - extraArgs: ["--build-system=swiftbuild"], - env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"], - buildSystem: .native, - ) + @Test( + .IssueWindowsLongPath, + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms) + ) + func rootPackageWithConditionals( + data: BuildData, + ) async throws { + try await withKnownIssue { + try await fixture(name: "Miscellaneous/RootPackageWithConditionals") { path in + let (_, stderr) = try await executeSwiftBuild( + path, + configuration: data.config, + env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"], + buildSystem: data.buildSystem, + ) + if data.buildSystem == .native { + let errors = stderr.components(separatedBy: .newlines).filter { !$0.contains("[logging] misuse") && !$0.isEmpty } + .filter { !$0.contains("Unable to locate libSwiftScan") } + #expect(errors == []) + } + } + } when: { + ProcessInfo.hostOperatingSystem == .windows && data.buildSystem == .swiftbuild } } }