From 2d6452990a713027a375ef09967c037ca5c2f112 Mon Sep 17 00:00:00 2001 From: Sam Khouri Date: Tue, 4 Feb 2025 09:33:29 -0500 Subject: [PATCH 1/4] Tests: Augment Command Tests Augment Commands Tests to run against the Native and Swift Build build systems. - BuildCommandTests - TestCommandTests - RunCommandTests - APIDiffTests - PackageCommandTests Also, - Add a new BuildCommandTest and RunCommandTest test case that ensures a package with target conditionals builds and runs successfully - update BuildSystemProvider.Kind to define a "useXcodeBuildSystemPath" variable instead of sprinkling the `buildSystem == .xcode` all over the place - Augment the Swift Build integration test to run `swift test` TODO: - Instead of marking test failures as "Skipped", See if we can mark them as "expected fail" so we are forced to update the test once the production code has been update to support the "feature". more update' --- .../.gitignore | 8 + .../Package.swift | 56 +++++ .../Sources/AllPlatforms/AllPlatforms.swift | 20 ++ .../Sources/CLibArchive/module.modulemap | 5 + .../Sources/CLibArchive/shim.h | 2 + .../Sources/ExecutableTargetWhen/main.swift | 19 ++ .../Sources/LinuxOnly/Linux.swift | 11 + .../Sources/MacOSOnly/MacOS.swift | 8 + .../Sources/WindowsOnly/Windows.swift | 9 + .../Tests/IntegrationTests/SwiftPMTests.swift | 24 +- .../BuildSystem/BuildSystem.swift | 13 + .../SwiftBuildSupport/SwiftBuildSystem.swift | 2 + Sources/_InternalTestSupport/misc.swift | 23 +- Tests/CommandsTests/APIDiffTests.swift | 37 ++- Tests/CommandsTests/BuildCommandTests.swift | 165 ++++++++++++- Tests/CommandsTests/CommandsTestCase.swift | 19 ++ Tests/CommandsTests/PackageCommandTests.swift | 229 +++++++++++------- Tests/CommandsTests/RunCommandTests.swift | 107 +++++++- Tests/CommandsTests/TestCommandTests.swift | 109 +++++++-- 19 files changed, 723 insertions(+), 143 deletions(-) create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/.gitignore create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Package.swift create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/AllPlatforms/AllPlatforms.swift create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/module.modulemap create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/shim.h create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/ExecutableTargetWhen/main.swift create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/LinuxOnly/Linux.swift create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/MacOSOnly/MacOS.swift create mode 100644 Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/WindowsOnly/Windows.swift diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/.gitignore b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/.gitignore new file mode 100644 index 00000000000..0023a534063 --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/.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/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Package.swift b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Package.swift new file mode 100644 index 00000000000..2703035970e --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Package.swift @@ -0,0 +1,56 @@ +// swift-tools-version: 6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "ExecutableTargetWhen", + products: [ + .executable( + name: "test", + targets: ["ExecutableTargetWhen"] + ) + ], + 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. + .executableTarget( + name: "ExecutableTargetWhen", + dependencies: [ + .target(name:"LinuxOnly", condition: .when(platforms:[.linux])), + .target(name:"MacOSOnly", condition: .when(platforms:[.macOS])), + .target(name:"WindowsOnly", condition: .when(platforms:[.windows])), + .target(name:"AllPlatforms") + ] + ), + .target( + name: "AllPlatforms" + ), + .target( + name: "LinuxOnly", + dependencies: [ + "CLibArchive", + "AllPlatforms" + ] + ), + .target( + name: "MacOSOnly", + dependencies: [ + "AllPlatforms" + ] + ), + .target( + name: "WindowsOnly", + dependencies: [ + "AllPlatforms" + ] + ), + .systemLibrary( + name: "CLibArchive", + pkgConfig: "libarchive", + providers: [ + .apt(["libarchive-dev"]), + ] + ), + ] +) diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/AllPlatforms/AllPlatforms.swift b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/AllPlatforms/AllPlatforms.swift new file mode 100644 index 00000000000..64ceb49f6a9 --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/AllPlatforms/AllPlatforms.swift @@ -0,0 +1,20 @@ +public func getPlatform() throws -> String { + #if os(Windows) + return "Windows" + #else + #if os(macOS) + return "macOS" + #else + #if os(linux) + return "Linux" + #else + return "Unknown platform" + #endif + #endif + #endif +} + + +public protocol MyProtocol { + static var name: String { get } +} \ No newline at end of file diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/module.modulemap b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/module.modulemap new file mode 100644 index 00000000000..3a6e9c4c9e7 --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/module.modulemap @@ -0,0 +1,5 @@ +module CLibArchive [system] { + header "shim.h" + link "archive" + export * +} \ No newline at end of file diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/shim.h b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/shim.h new file mode 100644 index 00000000000..da4d194b61b --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/CLibArchive/shim.h @@ -0,0 +1,2 @@ +#include "archive.h" +#include "archive_entry.h" diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/ExecutableTargetWhen/main.swift b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/ExecutableTargetWhen/main.swift new file mode 100644 index 00000000000..7a27703cace --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/ExecutableTargetWhen/main.swift @@ -0,0 +1,19 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import AllPlatforms + +#if os(Windows) + import WindowsOnly +#else + #if os(macOS) + import MacOSOnly + #else + #if os(linux) + import LinuxOnly + #endif + #endif +#endif + +let platform = try getPlatform() +print("Hello, world on \(platform)! OSplatform: \(OSPlatform.name)") diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/LinuxOnly/Linux.swift b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/LinuxOnly/Linux.swift new file mode 100644 index 00000000000..bb73cdda4b8 --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/LinuxOnly/Linux.swift @@ -0,0 +1,11 @@ +import CLibArchive + +import AllPlatforms + +public struct OSPlatform: MyProtocol { + + public static var name: String { + return "Linux" + } + +} \ No newline at end of file diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/MacOSOnly/MacOS.swift b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/MacOSOnly/MacOS.swift new file mode 100644 index 00000000000..6c292bdde6d --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/MacOSOnly/MacOS.swift @@ -0,0 +1,8 @@ +import AllPlatforms +public struct OSPlatform: MyProtocol { + + public static var name: String { + return "macOS" + } + +} \ No newline at end of file diff --git a/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/WindowsOnly/Windows.swift b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/WindowsOnly/Windows.swift new file mode 100644 index 00000000000..4098b24badc --- /dev/null +++ b/Fixtures/Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional/Sources/WindowsOnly/Windows.swift @@ -0,0 +1,9 @@ +import AllPlatforms + +public struct OSPlatform: MyProtocol { + + public static var name: String { + return "Windows" + } + +} \ No newline at end of file diff --git a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift index 8ef4c6e9504..a050ee8c9d0 100644 --- a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift +++ b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift @@ -57,11 +57,25 @@ final class SwiftPMTests: XCTestCase { #endif // Test SwiftBuildSystem - try withTemporaryDirectory { tmpDir in - let packagePath = tmpDir.appending(component: "foo") - try localFileSystem.createDirectory(packagePath) - try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable") - try sh(swiftBuild, "--package-path", packagePath, "--build-system", "swiftbuild") + do { + try withTemporaryDirectory { tmpDir in + let packagePath = tmpDir.appending(component: "foo") + try localFileSystem.createDirectory(packagePath) + try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable") + try sh(swiftBuild, "--package-path", packagePath, "--build-system", "swiftbuild") + let (stdout, stderr) = try sh(swiftRun, "--package-path", packagePath, "--build-system", "swiftbuild") + XCTAssertMatch(stdout, .contains("Hello, world!")) + } + } + + do { + try withTemporaryDirectory { tmpDir in + let packagePath = tmpDir.appending(component: "foo") + try localFileSystem.createDirectory(packagePath) + try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "library") + try sh(swiftBuild, "--package-path", packagePath, "--build-system", "swiftbuild") + try sh(swiftTest, "--package-path", packagePath, "--build-system", "swiftbuild") + } } } diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index c4a79954872..abcb98c94f9 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -191,3 +191,16 @@ public enum BuildSystemUtilities { return try AbsolutePath(validating: env, relativeTo: workingDir) } } + + +extension BuildSystemProvider.Kind { + + public var useXcodeBuildSystemPath: Bool { + switch self { + case .native: return false + case .swiftbuild: return true + case .xcode: return true + } + } + +} \ No newline at end of file diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index d451bf92475..f4dd20b2d0f 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -242,6 +242,8 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { do { try await withSession(service: service, name: buildParameters.pifManifest.pathString) { session, _ in + self.outputStream.send("Building for \(self.buildParameters.configuration == .debug ? "debugging" : "production")...\n") + // Load the workspace, and set the system information to the default do { try await session.loadWorkspace(containerPath: self.buildParameters.pifManifest.pathString) diff --git a/Sources/_InternalTestSupport/misc.swift b/Sources/_InternalTestSupport/misc.swift index d2043f3dbb7..cdc71a6c910 100644 --- a/Sources/_InternalTestSupport/misc.swift +++ b/Sources/_InternalTestSupport/misc.swift @@ -117,6 +117,10 @@ public func testWithTemporaryDirectory( } } +public enum TestError: Error { + case platformNotSupported +} + @discardableResult public func fixture( name: String, createGitRepo: Bool = true, @@ -252,7 +256,7 @@ public func getBuildSystemArgs(for buildSystem: BuildSystemProvider.Kind?) -> [S @discardableResult public func executeSwiftBuild( - _ packagePath: AbsolutePath, + _ packagePath: AbsolutePath?, configuration: Configuration = .Debug, extraArgs: [String] = [], Xcc: [String] = [], @@ -274,8 +278,8 @@ public func executeSwiftBuild( @discardableResult public func executeSwiftRun( - _ packagePath: AbsolutePath, - _ executable: String, + _ packagePath: AbsolutePath?, + _ executable: String?, configuration: Configuration = .Debug, extraArgs: [String] = [], Xcc: [String] = [], @@ -292,13 +296,15 @@ public func executeSwiftRun( Xswiftc: Xswiftc, buildSystem: buildSystem ) - args.append(executable) + if let executable { + args.append(executable) + } return try await SwiftPM.Run.execute(args, packagePath: packagePath, env: env) } @discardableResult public func executeSwiftPackage( - _ packagePath: AbsolutePath, + _ packagePath: AbsolutePath?, configuration: Configuration = .Debug, extraArgs: [String] = [], Xcc: [String] = [], @@ -320,7 +326,7 @@ public func executeSwiftPackage( @discardableResult public func executeSwiftPackageRegistry( - _ packagePath: AbsolutePath, + _ packagePath: AbsolutePath?, configuration: Configuration = .Debug, extraArgs: [String] = [], Xcc: [String] = [], @@ -342,13 +348,14 @@ public func executeSwiftPackageRegistry( @discardableResult public func executeSwiftTest( - _ packagePath: AbsolutePath, + _ packagePath: AbsolutePath?, configuration: Configuration = .Debug, extraArgs: [String] = [], Xcc: [String] = [], Xld: [String] = [], Xswiftc: [String] = [], env: Environment? = nil, + throwIfCommandFails: Bool = false, buildSystem: BuildSystemProvider.Kind = .native ) async throws -> (stdout: String, stderr: String) { let args = swiftArgs( @@ -359,7 +366,7 @@ public func executeSwiftTest( Xswiftc: Xswiftc, buildSystem: buildSystem ) - return try await SwiftPM.Test.execute(args, packagePath: packagePath, env: env) + return try await SwiftPM.Test.execute(args, packagePath: packagePath, env: env, throwIfCommandFails: throwIfCommandFails) } private func swiftArgs( diff --git a/Tests/CommandsTests/APIDiffTests.swift b/Tests/CommandsTests/APIDiffTests.swift index 11a9158e43c..905af6df577 100644 --- a/Tests/CommandsTests/APIDiffTests.swift +++ b/Tests/CommandsTests/APIDiffTests.swift @@ -13,6 +13,7 @@ import Basics import Build import Commands +import SPMBuildCore @_spi(SwiftPMInternal) import DriverSupport @@ -24,7 +25,11 @@ import _InternalTestSupport import Workspace import XCTest -final class APIDiffTests: CommandsTestCase { +class APIDiffTestCase: CommandsBuildProviderTestCase { + override func setUpWithError() throws { + try XCTSkipIf(type(of: self) == APIDiffTestCase.self, "Pay no attention to the class behind the curtain.") + } + @discardableResult private func execute( _ args: [String], @@ -34,7 +39,12 @@ final class APIDiffTests: CommandsTestCase { var environment = env ?? [:] // don't ignore local packages when caching environment["SWIFTPM_TESTS_PACKAGECACHE"] = "1" - return try await SwiftPM.Package.execute(args, packagePath: packagePath, env: environment) + return try await executeSwiftPackage( + packagePath, + extraArgs: args, + env: environment, + buildSystem: buildSystemProvider + ) } func skipIfApiDigesterUnsupportedOrUnset() throws { @@ -449,3 +459,26 @@ final class APIDiffTests: CommandsTestCase { } } } + +class APIDiffNativeTests: APIDiffTestCase { + + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .native + } + + override func skipIfApiDigesterUnsupportedOrUnset() throws { + try super.skipIfApiDigesterUnsupportedOrUnset() + } + +} + +class APIDiffSwiftBuildTests: APIDiffTestCase { + + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .swiftbuild + } + + override func skipIfApiDigesterUnsupportedOrUnset() throws { + try super.skipIfApiDigesterUnsupportedOrUnset() + } +} \ No newline at end of file diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index ae3f22ff4bb..f30f7740d10 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -19,6 +19,7 @@ import PackageLoading import PackageModel import SPMBuildCore import _InternalTestSupport +import TSCTestSupport import Workspace import XCTest @@ -30,14 +31,24 @@ struct BuildResult { let moduleContents: [String] } -final class BuildCommandTests: CommandsTestCase { +class BuildCommandTestCases: CommandsBuildProviderTestCase { + + override func setUpWithError() throws { + try XCTSkipIf(type(of: self) == BuildCommandTestCases.self, "Pay no attention to the class behind the curtain.") + } + @discardableResult private func execute( _ args: [String] = [], environment: Environment? = nil, packagePath: AbsolutePath? = nil ) async throws -> (stdout: String, stderr: String) { - try await SwiftPM.Build.execute(args, packagePath: packagePath, env: environment) + return try await executeSwiftBuild( + packagePath, + extraArgs: args, + env: environment, + buildSystem: buildSystemProvider + ) } func build(_ args: [String], packagePath: AbsolutePath? = nil, isRelease: Bool = false, cleanAfterward: Bool = true) async throws -> BuildResult { @@ -62,7 +73,11 @@ final class BuildCommandTests: CommandsTestCase { let moduleContents = (try? localFileSystem.getDirectoryContents(binPath.appending(component: "Modules"))) ?? [] if cleanAfterward { - try! await SwiftPM.Package.execute(["clean"], packagePath: packagePath) + try! await executeSwiftPackage( + packagePath, + extraArgs: ["clean"], + buildSystem: buildSystemProvider + ) } return BuildResult( binPath: binPath, @@ -73,7 +88,11 @@ final class BuildCommandTests: CommandsTestCase { ) } catch { if cleanAfterward { - try! await SwiftPM.Package.execute(["clean"], packagePath: packagePath) + try! await executeSwiftPackage( + packagePath, + extraArgs: ["clean"], + buildSystem: buildSystemProvider, + ) } throw error } @@ -171,7 +190,7 @@ final class BuildCommandTests: CommandsTestCase { } } - func testBinPathAndSymlink() async throws { + func testBinSymlink() async throws { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) let targetPath = try fullPath.appending( @@ -205,7 +224,16 @@ final class BuildCommandTests: CommandsTestCase { "\(xcbuildTargetPath.appending(components: "Products", "Release").pathString)\n" ) #endif + } + } + func testSymlink() async throws { + try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in + let fullPath = try resolveSymlinks(fixturePath) + let targetPath = try fullPath.appending( + components: ".build", + UserToolchain.default.targetTriple.platformBuildPathComponent + ) // Test symlink. try await self.execute(packagePath: fullPath) XCTAssertEqual( @@ -232,7 +260,11 @@ final class BuildCommandTests: CommandsTestCase { do { let (_, stderr) = try await execute(["--product", "lib1"], packagePath: fullPath) - try await SwiftPM.Package.execute(["clean"], packagePath: fullPath) + try await executeSwiftPackage( + fullPath, + extraArgs:["clean"], + buildSystem: buildSystemProvider + ) XCTAssertMatch( stderr, .contains( @@ -422,7 +454,7 @@ final class BuildCommandTests: CommandsTestCase { 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 // full command lines. - let output = try await execute(["--build-system", buildSystem, "-c", "debug", "-v"], packagePath: fixturePath) + let output = try await execute(["-c", "debug", "-v"], packagePath: fixturePath) // In the case of the native build system check for the cross-compile target, only for macOS #if os(macOS) @@ -437,7 +469,7 @@ final class BuildCommandTests: CommandsTestCase { // Look for build completion message from the particular build system XCTAssertMatch( output.stdout, - try .contains("Build complete!") + .contains("Build complete!") ) } } @@ -555,6 +587,27 @@ final class BuildCommandTests: CommandsTestCase { } } + private func _testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfully(configuration: TSCTestSupport.Configuration) async throws { + try await fixture(name: "Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional") { fixturePath in + await XCTAssertBuilds( + fixturePath, + configurations: [configuration], + buildSystem: buildSystemProvider + ) + + // let result = try await execute([], packagePath: fixturePath) + // XCTAssertMatch(result.stdout, .contains("Build Complete")) + } + } + + func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { + try await self._testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfully(configuration: .Debug) + } + + func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { + try await self._testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfully(configuration: .Release) + } + func testSwiftGetVersion() async throws { try await fixture(name: "Miscellaneous/Simple") { fixturePath in func findSwiftGetVersionFile() throws -> AbsolutePath { @@ -707,13 +760,29 @@ final class BuildCommandTests: CommandsTestCase { // Test that no codecov directory is created if not specified when building. try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in _ = try await self.build(["--build-tests"], packagePath: path, cleanAfterward: false) - await XCTAssertAsyncThrowsError(try await SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path)) + await XCTAssertAsyncThrowsError( + try await executeSwiftTest( + path, + extraArgs: [ + "--skip-build", + "--enable-code-coverage", + ], + buildSystem: buildSystemProvider + ) + ) } // Test that enabling code coverage during building produces the expected folder. try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { path in let buildResult = try await self.build(["--build-tests", "--enable-code-coverage"], packagePath: path, cleanAfterward: false) - try await SwiftPM.Test.execute(["--skip-build", "--enable-code-coverage"], packagePath: path) + try await executeSwiftTest( + path, + extraArgs: [ + "--skip-build", + "--enable-code-coverage", + ], + buildSystem: buildSystemProvider + ) let codeCovPath = buildResult.binPath.appending("codecov") let codeCovFiles = try localFileSystem.getDirectoryContents(codeCovPath) XCTAssertGreaterThan(codeCovFiles.count, 0) @@ -757,3 +826,79 @@ final class BuildCommandTests: CommandsTestCase { } } + + +class BuildCommandNativeTests: BuildCommandTestCases { + + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .native + } + + override func testUsage() async throws { + try await super.testUsage() + } +} + +class BuildCommandSwiftBuildTests: BuildCommandTestCases { + + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .swiftbuild + } + + override func testNonReachableProductsAndTargetsFunctional() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testParseableInterfaces() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testGetTaskAllowEntitlement() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testCodeCoverage() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testAtMainSupport() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testAutomaticParseableInterfacesWithLibraryEvolution() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testImportOfMissedDepWarning() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testProductAndTarget() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testPrintLLBuildManifestJobGraph() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testSwiftGetVersion() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testBinSymlink() async throws { + try XCTSkip("Test failed. need to update expected path") + } + + override func testSymlink() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + + override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { + try XCTSkip("Test failed. needs to be investigated") + } + +} diff --git a/Tests/CommandsTests/CommandsTestCase.swift b/Tests/CommandsTests/CommandsTestCase.swift index 1348b1c68e0..7833efb24e7 100644 --- a/Tests/CommandsTests/CommandsTestCase.swift +++ b/Tests/CommandsTests/CommandsTestCase.swift @@ -13,6 +13,7 @@ import Basics import XCTest import _InternalTestSupport + class CommandsTestCase: XCTestCase { /// Original working directory before the test ran (if known). @@ -33,3 +34,21 @@ class CommandsTestCase: XCTestCase { // FIXME: We should also hoist the `execute()` helper function that the various test suites implement, but right now they all seem to have slightly different implementations, so that's a later project. } + +class CommandsBuildProviderTestCase: BuildSystemProviderTestCase { + /// Original working directory before the test ran (if known). + private var originalWorkingDirectory: AbsolutePath? = .none + let duplicateSymbolRegex = StringPattern.regex(".*One of the duplicates must be removed or renamed.") + + override func setUp() { + originalWorkingDirectory = localFileSystem.currentWorkingDirectory + } + + override func tearDown() { + if let originalWorkingDirectory { + try? localFileSystem.changeCurrentWorkingDirectory(to: originalWorkingDirectory) + } + } + + // FIXME: We should also hoist the `execute()` helper function that the various test suites implement, but right now they all seem to have slightly different implementations, so that's a later project. +} \ No newline at end of file diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index e2ee97b6b3d..0c16b9642a5 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -21,6 +21,7 @@ import PackageGraph import PackageLoading import PackageModel import SourceControl +import SPMBuildCore import _InternalTestSupport import Workspace import XCTest @@ -30,7 +31,11 @@ import class TSCBasic.BufferedOutputByteStream import enum TSCBasic.JSON import class Basics.AsyncProcess -final class PackageCommandTests: CommandsTestCase { +class PackageCommandTestCase: CommandsBuildProviderTestCase { + override func setUpWithError() throws { + try XCTSkipIf(type(of: self) == PackageCommandTestCase.self, "Pay no attention to the class behind the curtain.") + } + @discardableResult private func execute( _ args: [String] = [], @@ -40,7 +45,13 @@ final class PackageCommandTests: CommandsTestCase { var environment = env ?? [:] // don't ignore local packages when caching environment["SWIFTPM_TESTS_PACKAGECACHE"] = "1" - return try await SwiftPM.Package.execute(args, packagePath: packagePath, env: environment) + // return try await self.execute(args, packagePath: packagePath, env: environment) + return try await executeSwiftPackage( + packagePath, + extraArgs: args, + env: environment, + buildSystem: buildSystemProvider + ) } func testNoParameters() async throws { @@ -61,7 +72,8 @@ final class PackageCommandTests: CommandsTestCase { } func testSeeAlso() async throws { - let stdout = try await execute(["--help"]).stdout + // This test fails when `--build-system ` is provided, so directly invoke SwiftPM.Package.execute + let stdout = try await SwiftPM.Package.execute(["--help"]).stdout XCTAssertMatch(stdout, .contains("SEE ALSO: swift build, swift run, swift test")) } @@ -72,7 +84,8 @@ final class PackageCommandTests: CommandsTestCase { } func testVersion() async throws { - let stdout = try await execute(["--version"]).stdout + // This test fails when `--build-system ` is provided, so directly invoke SwiftPM.Package.execute + let stdout = try await SwiftPM.Package.execute(["--version"]).stdout XCTAssertMatch(stdout, .regex(#"Swift Package Manager -( \w+ )?\d+.\d+.\d+(-\w+)?"#)) } @@ -346,7 +359,7 @@ final class PackageCommandTests: CommandsTestCase { func testDescribe() async throws { try await fixture(name: "Miscellaneous/ExeTest") { fixturePath in // Generate the JSON description. - let (jsonOutput, _) = try await SwiftPM.Package.execute(["describe", "--type=json"], packagePath: fixturePath) + let (jsonOutput, _) = try await self.execute(["describe", "--type=json"], packagePath: fixturePath) let json = try JSON(bytes: ByteString(encodingAsUTF8: jsonOutput)) // Check that tests don't appear in the product memberships. @@ -359,7 +372,7 @@ final class PackageCommandTests: CommandsTestCase { try await fixture(name: "CFamilyTargets/SwiftCMixed") { fixturePath in // Generate the JSON description. - let (jsonOutput, _) = try await SwiftPM.Package.execute(["describe", "--type=json"], packagePath: fixturePath) + let (jsonOutput, _) = try await self.execute(["describe", "--type=json"], packagePath: fixturePath) let json = try JSON(bytes: ByteString(encodingAsUTF8: jsonOutput)) // Check that the JSON description contains what we expect it to. @@ -386,7 +399,7 @@ final class PackageCommandTests: CommandsTestCase { XCTAssertEqual(jsonTarget2["product_memberships"]?.array?[0].stringValue, "CExec") // Generate the text description. - let (textOutput, _) = try await SwiftPM.Package.execute(["describe", "--type=text"], packagePath: fixturePath) + let (textOutput, _) = try await self.execute(["describe", "--type=text"], packagePath: fixturePath) let textChunks = textOutput.components(separatedBy: "\n").reduce(into: [""]) { chunks, line in // Split the text into chunks based on presence or absence of leading whitespace. if line.hasPrefix(" ") == chunks[chunks.count-1].hasPrefix(" ") { @@ -440,7 +453,7 @@ final class PackageCommandTests: CommandsTestCase { try await fixture(name: "DependencyResolution/External/Simple/Bar") { fixturePath in // Generate the JSON description. - let (jsonOutput, _) = try await SwiftPM.Package.execute(["describe", "--type=json"], packagePath: fixturePath) + let (jsonOutput, _) = try await self.execute(["describe", "--type=json"], packagePath: fixturePath) let json = try JSON(bytes: ByteString(encodingAsUTF8: jsonOutput)) // Check that product dependencies and memberships are as expected. @@ -456,7 +469,7 @@ final class PackageCommandTests: CommandsTestCase { func testDescribePackageUsingPlugins() async throws { try await fixture(name: "Miscellaneous/Plugins/MySourceGenPlugin") { fixturePath in // Generate the JSON description. - let (stdout, _) = try await SwiftPM.Package.execute(["describe", "--type=json"], packagePath: fixturePath) + let (stdout, _) = try await self.execute(["describe", "--type=json"], packagePath: fixturePath) let json = try JSON(bytes: ByteString(encodingAsUTF8: stdout)) // Check the contents of the JSON. @@ -512,7 +525,7 @@ final class PackageCommandTests: CommandsTestCase { let arguments = withPrettyPrinting ? ["dump-symbol-graph", "--pretty-print"] : ["dump-symbol-graph"] - let result = try await SwiftPM.Package.execute(arguments, packagePath: path, env: ["SWIFT_SYMBOLGRAPH_EXTRACT": symbolGraphExtractorPath.pathString]) + let result = try await self.execute(arguments, packagePath: path, env: ["SWIFT_SYMBOLGRAPH_EXTRACT": symbolGraphExtractorPath.pathString]) let enumerator = try XCTUnwrap(FileManager.default.enumerator(at: URL(fileURLWithPath: path.pathString), includingPropertiesForKeys: nil), file: file, line: line) var symbolGraphURL: URL? @@ -588,11 +601,11 @@ final class PackageCommandTests: CommandsTestCase { func testShowExecutables() async throws { try await fixture(name: "Miscellaneous/ShowExecutables") { fixturePath in let packageRoot = fixturePath.appending("app") - let (textOutput, _) = try await SwiftPM.Package.execute(["show-executables", "--format=flatlist"], packagePath: packageRoot) + let (textOutput, _) = try await self.execute(["show-executables", "--format=flatlist"], packagePath: packageRoot) XCTAssert(textOutput.contains("dealer\n")) XCTAssert(textOutput.contains("deck (deck-of-playing-cards)\n")) - let (jsonOutput, _) = try await SwiftPM.Package.execute(["show-executables", "--format=json"], packagePath: packageRoot) + let (jsonOutput, _) = try await self.execute(["show-executables", "--format=json"], packagePath: packageRoot) let json = try JSON(bytes: ByteString(encodingAsUTF8: jsonOutput)) guard case let .array(contents) = json else { XCTFail("unexpected result"); return } @@ -623,10 +636,10 @@ final class PackageCommandTests: CommandsTestCase { func testShowDependencies() async throws { try await fixture(name: "DependencyResolution/External/Complex") { fixturePath in let packageRoot = fixturePath.appending("app") - let (textOutput, _) = try await SwiftPM.Package.execute(["show-dependencies", "--format=text"], packagePath: packageRoot) + let (textOutput, _) = try await self.execute(["show-dependencies", "--format=text"], packagePath: packageRoot) XCTAssert(textOutput.contains("FisherYates@1.2.3")) - let (jsonOutput, _) = try await SwiftPM.Package.execute(["show-dependencies", "--format=json"], packagePath: packageRoot) + let (jsonOutput, _) = try await self.execute(["show-dependencies", "--format=json"], packagePath: packageRoot) let json = try JSON(bytes: ByteString(encodingAsUTF8: jsonOutput)) guard case let .dictionary(contents) = json else { XCTFail("unexpected result"); return } guard case let .string(name)? = contents["name"] else { XCTFail("unexpected result"); return } @@ -1206,12 +1219,12 @@ final class PackageCommandTests: CommandsTestCase { try await fixture(name: "Miscellaneous/PackageEdit") { fixturePath in let fooPath = fixturePath.appending("foo") func build() async throws -> (stdout: String, stderr: String) { - return try await SwiftPM.Build.execute(packagePath: fooPath) + return try await executeSwiftBuild(fooPath) } // Put bar and baz in edit mode. - _ = try await SwiftPM.Package.execute(["edit", "bar", "--branch", "bugfix"], packagePath: fooPath) - _ = try await SwiftPM.Package.execute(["edit", "baz", "--branch", "bugfix"], packagePath: fooPath) + _ = try await self.execute(["edit", "bar", "--branch", "bugfix"], packagePath: fooPath) + _ = try await self.execute(["edit", "baz", "--branch", "bugfix"], packagePath: fooPath) // Path to the executable. let exec = [fooPath.appending(components: ".build", try UserToolchain.default.targetTriple.platformBuildPathComponent, "debug", "foo").pathString] @@ -1238,7 +1251,7 @@ final class PackageCommandTests: CommandsTestCase { // It shouldn't be possible to unedit right now because of uncommitted changes. do { - _ = try await SwiftPM.Package.execute(["unedit", "bar"], packagePath: fooPath) + _ = try await self.execute(["unedit", "bar"], packagePath: fooPath) XCTFail("Unexpected unedit success") } catch {} @@ -1247,7 +1260,7 @@ final class PackageCommandTests: CommandsTestCase { // It shouldn't be possible to unedit right now because of unpushed changes. do { - _ = try await SwiftPM.Package.execute(["unedit", "bar"], packagePath: fooPath) + _ = try await self.execute(["unedit", "bar"], packagePath: fooPath) XCTFail("Unexpected unedit success") } catch {} @@ -1255,11 +1268,11 @@ final class PackageCommandTests: CommandsTestCase { try editsRepo.push(remote: "origin", branch: "bugfix") // We should be able to unedit now. - _ = try await SwiftPM.Package.execute(["unedit", "bar"], packagePath: fooPath) + _ = try await self.execute(["unedit", "bar"], packagePath: fooPath) // Test editing with a path i.e. ToT development. let bazTot = fixturePath.appending("tot") - try await SwiftPM.Package.execute(["edit", "baz", "--path", bazTot.pathString], packagePath: fooPath) + try await self.execute(["edit", "baz", "--path", bazTot.pathString], packagePath: fooPath) XCTAssertTrue(localFileSystem.exists(bazTot)) XCTAssertTrue(localFileSystem.isSymlink(bazEditsPath)) @@ -1270,12 +1283,12 @@ final class PackageCommandTests: CommandsTestCase { try localFileSystem.writeFileContents(bazTotPackageFile, string: content) // Unediting baz will remove the symlink but not the checked out package. - try await SwiftPM.Package.execute(["unedit", "baz"], packagePath: fooPath) + try await self.execute(["unedit", "baz"], packagePath: fooPath) XCTAssertTrue(localFileSystem.exists(bazTot)) XCTAssertFalse(localFileSystem.isSymlink(bazEditsPath)) // Check that on re-editing with path, we don't make a new clone. - try await SwiftPM.Package.execute(["edit", "baz", "--path", bazTot.pathString], packagePath: fooPath) + try await self.execute(["edit", "baz", "--path", bazTot.pathString], packagePath: fooPath) XCTAssertTrue(localFileSystem.isSymlink(bazEditsPath)) XCTAssertEqual(try localFileSystem.readFileContents(bazTotPackageFile), content) } @@ -1331,7 +1344,7 @@ final class PackageCommandTests: CommandsTestCase { @discardableResult func execute(_ args: String..., printError: Bool = true) async throws -> String { - return try await SwiftPM.Package.execute([] + args, packagePath: fooPath).stdout + return try await self.execute([] + args, packagePath: fooPath).stdout } try await execute("update") @@ -1392,7 +1405,7 @@ final class PackageCommandTests: CommandsTestCase { ).pathString] // Build and check. - _ = try await SwiftPM.Build.execute(packagePath: fooPath) + _ = try await executeSwiftBuild(fooPath) try await XCTAssertAsyncEqual(try await AsyncProcess.checkNonZeroExit(arguments: exec).spm_chomp(), "\(5)") // Get path to `bar` checkout. @@ -1435,7 +1448,7 @@ final class PackageCommandTests: CommandsTestCase { @discardableResult func execute(_ args: String...) async throws -> String { - return try await SwiftPM.Package.execute([] + args, packagePath: fooPath).stdout + return try await self.execute([] + args, packagePath: fooPath).stdout } // Try to pin bar. @@ -1766,7 +1779,7 @@ final class PackageCommandTests: CommandsTestCase { try await execute(["config", "set-mirror", "--original", "https://scm.com/org/foo", "--mirror", "https://scm.com/org/bar"], packagePath: packageRoot) XCTAssertTrue(fs.isFile(configFile)) - let (stdout, _) = try await SwiftPM.Package.execute(["dump-package"], packagePath: packageRoot) + let (stdout, _) = try await self.execute(["dump-package"], packagePath: packageRoot) XCTAssertMatch(stdout, .contains("https://scm.com/org/bar")) XCTAssertNoMatch(stdout, .contains("https://scm.com/org/foo")) } @@ -1809,7 +1822,7 @@ final class PackageCommandTests: CommandsTestCase { try await execute(["config", "set-mirror", "--original", "https://scm.com/org/foo", "--mirror", "org.bar"], packagePath: packageRoot) XCTAssertTrue(fs.isFile(configFile)) - let (stdout, _) = try await SwiftPM.Package.execute(["dump-package"], packagePath: packageRoot) + let (stdout, _) = try await self.execute(["dump-package"], packagePath: packageRoot) XCTAssertMatch(stdout, .contains("org.bar")) XCTAssertNoMatch(stdout, .contains("https://scm.com/org/foo")) } @@ -1852,7 +1865,7 @@ final class PackageCommandTests: CommandsTestCase { try await execute(["config", "set-mirror", "--original", "org.foo", "--mirror", "https://scm.com/org/bar"], packagePath: packageRoot) XCTAssertTrue(fs.isFile(configFile)) - let (stdout, _) = try await SwiftPM.Package.execute(["dump-package"], packagePath: packageRoot) + let (stdout, _) = try await self.execute(["dump-package"], packagePath: packageRoot) XCTAssertMatch(stdout, .contains("https://scm.com/org/bar")) XCTAssertNoMatch(stdout, .contains("org.foo")) } @@ -1882,7 +1895,7 @@ final class PackageCommandTests: CommandsTestCase { // Invoke `swift-package`, passing in the overriding `PATH` environment variable. let packageRoot = fixturePath.appending("Library") let patchedPATH = fakeBinDir.pathString + ":" + ProcessInfo.processInfo.environment["PATH"]! - let (stdout, _) = try await SwiftPM.Package.execute(["dump-package"], packagePath: packageRoot, env: ["PATH": patchedPATH]) + let (stdout, _) = try await self.execute(["dump-package"], packagePath: packageRoot, env: ["PATH": patchedPATH]) // Check that the wrong tools weren't invoked. We can't just check the exit code because of fallbacks. XCTAssertNoMatch(stdout, .contains("wrong xcrun invoked")) @@ -1990,7 +2003,10 @@ final class PackageCommandTests: CommandsTestCase { // Invoke it, and check the results. let args = staticStdlib ? ["--static-swift-stdlib"] : [] - let (stdout, stderr) = try await SwiftPM.Build.execute(args, packagePath: packageDir) + let (stdout, stderr) = try await executeSwiftBuild( + packageDir, + extraArgs: args + ) XCTAssert(stdout.contains("Build complete!")) // We expect a warning about `library.bar` but not about `library.foo`. @@ -2056,7 +2072,7 @@ final class PackageCommandTests: CommandsTestCase { ) // Invoke it, and check the results. - await XCTAssertAsyncThrowsError(try await SwiftPM.Build.execute(["-v"], packagePath: packageDir)) { error in + await XCTAssertAsyncThrowsError(try await executeSwiftBuild(packageDir, extraArgs: ["-v"])) { error in guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2073,34 +2089,34 @@ final class PackageCommandTests: CommandsTestCase { // Running without arguments or options do { - let (stdout, _) = try await SwiftPM.Package.execute(["archive-source"], packagePath: packageRoot) + let (stdout, _) = try await self.execute(["archive-source"], packagePath: packageRoot) XCTAssert(stdout.contains("Created Bar.zip"), #"actual: "\#(stdout)""#) } // Running without arguments or options again, overwriting existing archive do { - let (stdout, _) = try await SwiftPM.Package.execute(["archive-source"], packagePath: packageRoot) + let (stdout, _) = try await self.execute(["archive-source"], packagePath: packageRoot) XCTAssert(stdout.contains("Created Bar.zip"), #"actual: "\#(stdout)""#) } // Running with output as absolute path within package root do { let destination = packageRoot.appending("Bar-1.2.3.zip") - let (stdout, _) = try await SwiftPM.Package.execute(["archive-source", "--output", destination.pathString], packagePath: packageRoot) + let (stdout, _) = try await self.execute(["archive-source", "--output", destination.pathString], packagePath: packageRoot) XCTAssert(stdout.contains("Created Bar-1.2.3.zip"), #"actual: "\#(stdout)""#) } // Running with output is outside the package root try await withTemporaryDirectory { tempDirectory in let destination = tempDirectory.appending("Bar-1.2.3.zip") - let (stdout, _) = try await SwiftPM.Package.execute(["archive-source", "--output", destination.pathString], packagePath: packageRoot) + let (stdout, _) = try await self.execute(["archive-source", "--output", destination.pathString], packagePath: packageRoot) XCTAssert(stdout.hasPrefix("Created /"), #"actual: "\#(stdout)""#) XCTAssert(stdout.contains("Bar-1.2.3.zip"), #"actual: "\#(stdout)""#) } // Running without arguments or options in non-package directory do { - await XCTAssertAsyncThrowsError(try await SwiftPM.Package.execute(["archive-source"], packagePath: fixturePath)) { error in + await XCTAssertAsyncThrowsError(try await self.execute(["archive-source"], packagePath: fixturePath)) { error in guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2111,7 +2127,7 @@ final class PackageCommandTests: CommandsTestCase { // Running with output as absolute path to existing directory do { let destination = AbsolutePath.root - await XCTAssertAsyncThrowsError(try await SwiftPM.Package.execute(["archive-source", "--output", destination.pathString], packagePath: packageRoot)) { error in + await XCTAssertAsyncThrowsError(try await self.execute(["archive-source", "--output", destination.pathString], packagePath: packageRoot)) { error in guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2332,25 +2348,25 @@ final class PackageCommandTests: CommandsTestCase { // Check that we can invoke the plugin with the "plugin" subcommand. do { - let (stdout, _) = try await SwiftPM.Package.execute(["plugin", "mycmd"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["plugin", "mycmd"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("This is MyCommandPlugin.")) } // Check that we can also invoke it without the "plugin" subcommand. do { - let (stdout, _) = try await SwiftPM.Package.execute(["mycmd"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["mycmd"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("This is MyCommandPlugin.")) } // Testing listing the available command plugins. do { - let (stdout, _) = try await SwiftPM.Package.execute(["plugin", "--list"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["plugin", "--list"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("‘mycmd’ (plugin ‘MyPlugin’ in package ‘MyPackage’)")) } // Check that we get the expected error if trying to invoke a plugin with the wrong name. do { - await XCTAssertAsyncThrowsError(try await SwiftPM.Package.execute(["my-nonexistent-cmd"], packagePath: packageDir)) { error in + await XCTAssertAsyncThrowsError(try await self.execute(["my-nonexistent-cmd"], packagePath: packageDir)) { error in guard case SwiftPMError.executionFailure(_, _, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2360,7 +2376,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that the .docc file was properly vended to the plugin. do { - let (stdout, _) = try await SwiftPM.Package.execute(["mycmd", "--target", "MyLibrary"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["mycmd", "--target", "MyLibrary"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Sources/MyLibrary/library.swift: source")) XCTAssertMatch(stdout, .contains("Sources/MyLibrary/test.docc: unknown")) } @@ -2368,13 +2384,13 @@ final class PackageCommandTests: CommandsTestCase { // Check that the initial working directory is what we expected. do { let workingDirectory = FileManager.default.currentDirectoryPath - let (stdout, _) = try await SwiftPM.Package.execute(["mycmd"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["mycmd"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Initial working directory: \(workingDirectory)")) } // Check that information about the dependencies was properly sent to the plugin. do { - let (stdout, _) = try await SwiftPM.Package.execute(["mycmd", "--target", "MyLibrary"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["mycmd", "--target", "MyLibrary"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("dependency HelperPackage: local")) } } @@ -2385,7 +2401,7 @@ final class PackageCommandTests: CommandsTestCase { try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") try await fixture(name: "Miscellaneous/Plugins/AmbiguousCommands") { fixturePath in - let (stdout, _) = try await SwiftPM.Package.execute(["plugin", "--package", "A", "A"], packagePath: fixturePath) + let (stdout, _) = try await self.execute(["plugin", "--package", "A", "A"], packagePath: fixturePath) XCTAssertMatch(stdout, .contains("Hello A!")) } } @@ -2407,13 +2423,13 @@ final class PackageCommandTests: CommandsTestCase { try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in func runPlugin(flags: [String], diagnostics: [String], completion: (String, String) -> Void) async throws { - let (stdout, stderr) = try await SwiftPM.Package.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) + let (stdout, stderr) = try await self.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) completion(stdout, stderr) } // Diagnostics.error causes SwiftPM to return a non-zero exit code, but we still need to check stdout and stderr func runPluginWithError(flags: [String], diagnostics: [String], completion: (String, String) -> Void) async throws { - await XCTAssertAsyncThrowsError(try await SwiftPM.Package.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"])) { error in + await XCTAssertAsyncThrowsError(try await self.execute(flags + ["print-diagnostics"] + diagnostics, packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"])) { error in guard case SwiftPMError.executionFailure(_, let stdout, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2555,35 +2571,35 @@ final class PackageCommandTests: CommandsTestCase { // By default, a plugin-requested build produces a debug binary try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let _ = try await SwiftPM.Package.execute(["-c", "release", "build-target"], packagePath: fixturePath) + let _ = try await self.execute(["-c", "release", "build-target"], packagePath: fixturePath) AssertIsExecutableFile(fixturePath.appending(components: debugTarget)) AssertNotExists(fixturePath.appending(components: releaseTarget)) } // If the plugin specifies a debug binary, that is what will be built, regardless of overall configuration try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let _ = try await SwiftPM.Package.execute(["-c", "release", "build-target", "build-debug"], packagePath: fixturePath) + let _ = try await self.execute(["-c", "release", "build-target", "build-debug"], packagePath: fixturePath) AssertIsExecutableFile(fixturePath.appending(components: debugTarget)) AssertNotExists(fixturePath.appending(components: releaseTarget)) } // If the plugin requests a release binary, that is what will be built, regardless of overall configuration try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let _ = try await SwiftPM.Package.execute(["-c", "debug", "build-target", "build-release"], packagePath: fixturePath) + let _ = try await self.execute(["-c", "debug", "build-target", "build-release"], packagePath: fixturePath) AssertNotExists(fixturePath.appending(components: debugTarget)) AssertIsExecutableFile(fixturePath.appending(components: releaseTarget)) } // If the plugin inherits the overall build configuration, that is what will be built try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let _ = try await SwiftPM.Package.execute(["-c", "debug", "build-target", "build-inherit"], packagePath: fixturePath) + let _ = try await self.execute(["-c", "debug", "build-target", "build-inherit"], packagePath: fixturePath) AssertIsExecutableFile(fixturePath.appending(components: debugTarget)) AssertNotExists(fixturePath.appending(components: releaseTarget)) } // If the plugin inherits the overall build configuration, that is what will be built try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let _ = try await SwiftPM.Package.execute(["-c", "release", "build-target", "build-inherit"], packagePath: fixturePath) + let _ = try await self.execute(["-c", "release", "build-target", "build-inherit"], packagePath: fixturePath) AssertNotExists(fixturePath.appending(components: debugTarget)) AssertIsExecutableFile(fixturePath.appending(components: releaseTarget)) } @@ -2594,27 +2610,27 @@ final class PackageCommandTests: CommandsTestCase { // Overall configuration: debug, plugin build request: debug -> without testability try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - await XCTAssertAsyncNoThrow(try await SwiftPM.Package.execute(["-c", "debug", "check-testability", "InternalModule", "debug", "true"], packagePath: fixturePath)) + await XCTAssertAsyncNoThrow(try await self.execute(["-c", "debug", "check-testability", "InternalModule", "debug", "true"], packagePath: fixturePath)) } // Overall configuration: debug, plugin build request: release -> without testability try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - await XCTAssertAsyncNoThrow(try await SwiftPM.Package.execute(["-c", "debug", "check-testability", "InternalModule", "release", "false"], packagePath: fixturePath)) + await XCTAssertAsyncNoThrow(try await self.execute(["-c", "debug", "check-testability", "InternalModule", "release", "false"], packagePath: fixturePath)) } // Overall configuration: release, plugin build request: debug -> with testability try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - await XCTAssertAsyncNoThrow(try await SwiftPM.Package.execute(["-c", "release", "check-testability", "InternalModule", "debug", "true"], packagePath: fixturePath)) + await XCTAssertAsyncNoThrow(try await self.execute(["-c", "release", "check-testability", "InternalModule", "debug", "true"], packagePath: fixturePath)) } // Overall configuration: release, plugin build request: release -> with testability try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - await XCTAssertAsyncNoThrow(try await SwiftPM.Package.execute(["-c", "release", "check-testability", "InternalModule", "release", "false"], packagePath: fixturePath)) + await XCTAssertAsyncNoThrow(try await self.execute(["-c", "release", "check-testability", "InternalModule", "release", "false"], packagePath: fixturePath)) } // Overall configuration: release, plugin build request: release including tests -> with testability try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - await XCTAssertAsyncNoThrow(try await SwiftPM.Package.execute(["-c", "release", "check-testability", "all-with-tests", "release", "true"], packagePath: fixturePath)) + await XCTAssertAsyncNoThrow(try await self.execute(["-c", "release", "check-testability", "all-with-tests", "release", "true"], packagePath: fixturePath)) } } @@ -2638,7 +2654,7 @@ final class PackageCommandTests: CommandsTestCase { // Check than nothing is echoed when echoLogs is false try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Package.execute(["print-diagnostics", "build"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) + let (stdout, stderr) = try await self.execute(["print-diagnostics", "build"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, isEmpty) // Filter some unrelated output that could show up on stderr. let filteredStderr = stderr.components(separatedBy: "\n") @@ -2648,7 +2664,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that logs are returned to the plugin when echoLogs is false try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Package.execute(["print-diagnostics", "build", "printlogs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) + let (stdout, stderr) = try await self.execute(["print-diagnostics", "build", "printlogs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, containsLogtext) // Filter some unrelated output that could show up on stderr. let filteredStderr = stderr.components(separatedBy: "\n") @@ -2658,14 +2674,14 @@ final class PackageCommandTests: CommandsTestCase { // Check that logs echoed to the console (on stderr) when echoLogs is true try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Package.execute(["print-diagnostics", "build", "echologs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) + let (stdout, stderr) = try await self.execute(["print-diagnostics", "build", "echologs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, isEmpty) XCTAssertMatch(stderr, containsLogecho) } // Check that logs are returned to the plugin and echoed to the console (on stderr) when echoLogs is true try await fixture(name: "Miscellaneous/Plugins/CommandPluginTestStub") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Package.execute(["print-diagnostics", "build", "printlogs", "echologs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) + let (stdout, stderr) = try await self.execute(["print-diagnostics", "build", "printlogs", "echologs"], packagePath: fixturePath, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, containsLogtext) XCTAssertMatch(stderr, containsLogecho) } @@ -2707,7 +2723,7 @@ final class PackageCommandTests: CommandsTestCase { #if os(macOS) do { - await XCTAssertAsyncThrowsError(try await SwiftPM.Package.execute(["plugin", "Network"], packagePath: packageDir)) { error in + await XCTAssertAsyncThrowsError(try await self.execute(["plugin", "Network"], packagePath: packageDir)) { error in guard case SwiftPMError.executionFailure(_, let stdout, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2721,7 +2737,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that we don't get an error (and also are allowed to write to the package directory) if we pass `--allow-writing-to-package-directory`. do { - let (stdout, _) = try await SwiftPM.Package.execute(["plugin"] + remedy + ["Network"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["plugin"] + remedy + ["Network"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("hello world")) } } @@ -2838,7 +2854,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that we get an error if the plugin needs permission but if we don't give it to them. Note that sandboxing is only currently supported on macOS. #if os(macOS) do { - await XCTAssertAsyncThrowsError(try await SwiftPM.Package.execute(["plugin", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"])) { error in + await XCTAssertAsyncThrowsError(try await self.execute(["plugin", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"])) { error in guard case SwiftPMError.executionFailure(_, let stdout, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2852,7 +2868,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that we don't get an error (and also are allowed to write to the package directory) if we pass `--allow-writing-to-package-directory`. do { - let (stdout, stderr) = try await SwiftPM.Package.execute(["plugin", "--allow-writing-to-package-directory", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) + let (stdout, stderr) = try await self.execute(["plugin", "--allow-writing-to-package-directory", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) XCTAssertMatch(stdout, .contains("successfully created it")) XCTAssertNoMatch(stderr, .contains("error: Couldn’t create file at path")) } @@ -2860,7 +2876,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that we get an error if the plugin doesn't declare permission but tries to write anyway. Note that sandboxing is only currently supported on macOS. #if os(macOS) do { - await XCTAssertAsyncThrowsError(try await SwiftPM.Package.execute(["plugin", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "0"])) { error in + await XCTAssertAsyncThrowsError(try await self.execute(["plugin", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "0"])) { error in guard case SwiftPMError.executionFailure(_, let stdout, let stderr) = error else { return XCTFail("invalid error \(error)") } @@ -2872,21 +2888,21 @@ final class PackageCommandTests: CommandsTestCase { // Check default command with arguments do { - let (stdout, stderr) = try await SwiftPM.Package.execute(["--allow-writing-to-package-directory", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) + let (stdout, stderr) = try await self.execute(["--allow-writing-to-package-directory", "PackageScribbler"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) XCTAssertMatch(stdout, .contains("successfully created it")) XCTAssertNoMatch(stderr, .contains("error: Couldn’t create file at path")) } // Check plugin arguments after plugin name do { - let (stdout, stderr) = try await SwiftPM.Package.execute(["plugin", "PackageScribbler", "--allow-writing-to-package-directory"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) + let (stdout, stderr) = try await self.execute(["plugin", "PackageScribbler", "--allow-writing-to-package-directory"], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) XCTAssertMatch(stdout, .contains("successfully created it")) XCTAssertNoMatch(stderr, .contains("error: Couldn’t create file at path")) } // Check default command with arguments after plugin name do { - let (stdout, stderr) = try await SwiftPM.Package.execute(["PackageScribbler", "--allow-writing-to-package-directory", ], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) + let (stdout, stderr) = try await self.execute(["PackageScribbler", "--allow-writing-to-package-directory", ], packagePath: packageDir, env: ["DECLARE_PACKAGE_WRITING_PERMISSION": "1"]) XCTAssertMatch(stdout, .contains("successfully created it")) XCTAssertNoMatch(stderr, .contains("error: Couldn’t create file at path")) } @@ -2958,14 +2974,14 @@ final class PackageCommandTests: CommandsTestCase { // Check arguments do { - let (stdout, stderr) = try await SwiftPM.Package.execute(["plugin", "MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) + let (stdout, stderr) = try await self.execute(["plugin", "MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, .contains("success")) XCTAssertFalse(stderr.contains("error:")) } // Check default command arguments do { - let (stdout, stderr) = try await SwiftPM.Package.execute(["MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) + let (stdout, stderr) = try await self.execute(["MyPlugin", "--foo", "--help", "--version", "--verbose"], packagePath: packageDir, env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"]) XCTAssertMatch(stdout, .contains("success")) XCTAssertFalse(stderr.contains("error:")) } @@ -3058,7 +3074,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that if we don't pass any target, we successfully get symbol graph information for all targets in the package, and at different paths. do { - let (stdout, _) = try await SwiftPM.Package.execute(["generate-documentation"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["generate-documentation"], packagePath: packageDir) XCTAssertMatch(stdout, .and(.contains("MyLibrary:"), .contains("mypackage/MyLibrary"))) XCTAssertMatch(stdout, .and(.contains("MyCommand:"), .contains("mypackage/MyCommand"))) @@ -3066,7 +3082,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that if we pass a target, we successfully get symbol graph information for just the target we asked for. do { - let (stdout, _) = try await SwiftPM.Package.execute(["generate-documentation", "--target", "MyLibrary"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["generate-documentation", "--target", "MyLibrary"], packagePath: packageDir) XCTAssertMatch(stdout, .and(.contains("MyLibrary:"), .contains("mypackage/MyLibrary"))) XCTAssertNoMatch(stdout, .and(.contains("MyCommand:"), .contains("mypackage/MyCommand"))) } @@ -3187,7 +3203,7 @@ final class PackageCommandTests: CommandsTestCase { // Invoke the plugin with parameters choosing a verbose build of MyExecutable for debugging. do { - let (stdout, _) = try await SwiftPM.Package.execute(["my-build-tester", "--product", "MyExecutable", "--print-commands"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["my-build-tester", "--product", "MyExecutable", "--print-commands"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Building for debugging...")) XCTAssertNoMatch(stdout, .contains("Building for production...")) XCTAssertMatch(stdout, .contains("-module-name MyExecutable")) @@ -3200,7 +3216,7 @@ final class PackageCommandTests: CommandsTestCase { // Invoke the plugin with parameters choosing a concise build of MyExecutable for release. do { - let (stdout, _) = try await SwiftPM.Package.execute(["my-build-tester", "--product", "MyExecutable", "--release"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["my-build-tester", "--product", "MyExecutable", "--release"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Building for production...")) XCTAssertNoMatch(stdout, .contains("Building for debug...")) XCTAssertNoMatch(stdout, .contains("-module-name MyExecutable")) @@ -3212,7 +3228,7 @@ final class PackageCommandTests: CommandsTestCase { // Invoke the plugin with parameters choosing a verbose build of MyStaticLibrary for release. do { - let (stdout, _) = try await SwiftPM.Package.execute(["my-build-tester", "--product", "MyStaticLibrary", "--print-commands", "--release"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["my-build-tester", "--product", "MyStaticLibrary", "--print-commands", "--release"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Building for production...")) XCTAssertNoMatch(stdout, .contains("Building for debug...")) XCTAssertNoMatch(stdout, .contains("-module-name MyLibrary")) @@ -3224,7 +3240,7 @@ final class PackageCommandTests: CommandsTestCase { // Invoke the plugin with parameters choosing a verbose build of MyDynamicLibrary for release. do { - let (stdout, _) = try await SwiftPM.Package.execute(["my-build-tester", "--product", "MyDynamicLibrary", "--print-commands", "--release"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["my-build-tester", "--product", "MyDynamicLibrary", "--print-commands", "--release"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Building for production...")) XCTAssertNoMatch(stdout, .contains("Building for debug...")) XCTAssertNoMatch(stdout, .contains("-module-name MyLibrary")) @@ -3353,7 +3369,7 @@ final class PackageCommandTests: CommandsTestCase { ) // Check basic usage with filtering and code coverage. The plugin itself asserts a bunch of values. - try await SwiftPM.Package.execute(["my-test-tester"], packagePath: packageDir) + try await self.execute(["my-test-tester"], packagePath: packageDir) // We'll add checks for various error conditions here in a future commit. } @@ -3529,28 +3545,28 @@ final class PackageCommandTests: CommandsTestCase { // Check that a target doesn't include itself in its recursive dependencies. do { - let (stdout, _) = try await SwiftPM.Package.execute(["print-target-dependencies", "--target", "SecondTarget"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["print-target-dependencies", "--target", "SecondTarget"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Recursive dependencies of 'SecondTarget': [\"FirstTarget\"]")) XCTAssertMatch(stdout, .contains("Module kind of 'SecondTarget': generic")) } // Check that targets are not included twice in recursive dependencies. do { - let (stdout, _) = try await SwiftPM.Package.execute(["print-target-dependencies", "--target", "ThirdTarget"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["print-target-dependencies", "--target", "ThirdTarget"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Recursive dependencies of 'ThirdTarget': [\"FirstTarget\"]")) XCTAssertMatch(stdout, .contains("Module kind of 'ThirdTarget': generic")) } // Check that product dependencies work in recursive dependencies. do { - let (stdout, _) = try await SwiftPM.Package.execute(["print-target-dependencies", "--target", "FourthTarget"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["print-target-dependencies", "--target", "FourthTarget"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Recursive dependencies of 'FourthTarget': [\"FirstTarget\", \"SecondTarget\", \"ThirdTarget\", \"HelperLibrary\"]")) XCTAssertMatch(stdout, .contains("Module kind of 'FourthTarget': generic")) } // Check some of the other utility APIs. do { - let (stdout, _) = try await SwiftPM.Package.execute(["print-target-dependencies", "--target", "FifthTarget"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["print-target-dependencies", "--target", "FifthTarget"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("execProducts: [\"FifthTarget\"]")) XCTAssertMatch(stdout, .contains("swiftTargets: [\"FifthTarget\", \"FirstTarget\", \"FourthTarget\", \"SecondTarget\", \"TestTarget\", \"ThirdTarget\"]")) XCTAssertMatch(stdout, .contains("swiftSources: [\"library.swift\", \"library.swift\", \"library.swift\", \"library.swift\", \"main.swift\", \"tests.swift\"]")) @@ -3559,7 +3575,7 @@ final class PackageCommandTests: CommandsTestCase { // Check a test target. do { - let (stdout, _) = try await SwiftPM.Package.execute(["print-target-dependencies", "--target", "TestTarget"], packagePath: packageDir) + let (stdout, _) = try await self.execute(["print-target-dependencies", "--target", "TestTarget"], packagePath: packageDir) XCTAssertMatch(stdout, .contains("Recursive dependencies of 'TestTarget': [\"FirstTarget\", \"SecondTarget\"]")) XCTAssertMatch(stdout, .contains("Module kind of 'TestTarget': test")) } @@ -3654,7 +3670,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that building without options compiles both plugins and that the build proceeds. do { - let (stdout, _) = try await SwiftPM.Build.execute(packagePath: packageDir) + let (stdout, _) = try await executeSwiftBuild(packageDir) XCTAssertMatch(stdout, .contains("Compiling plugin MyBuildToolPlugin")) XCTAssertMatch(stdout, .contains("Compiling plugin MyCommandPlugin")) XCTAssertMatch(stdout, .contains("Building for debugging...")) @@ -3662,7 +3678,10 @@ final class PackageCommandTests: CommandsTestCase { // Check that building just one of them just compiles that plugin and doesn't build anything else. do { - let (stdout, _) = try await SwiftPM.Build.execute(["--target", "MyCommandPlugin"], packagePath: packageDir) + let (stdout, _) = try await executeSwiftBuild( + packageDir, + extraArgs: ["--target", "MyCommandPlugin"] + ) XCTAssertNoMatch(stdout, .contains("Compiling plugin MyBuildToolPlugin")) XCTAssertMatch(stdout, .contains("Compiling plugin MyCommandPlugin")) XCTAssertNoMatch(stdout, .contains("Building for debugging...")) @@ -3685,7 +3704,7 @@ final class PackageCommandTests: CommandsTestCase { // Check that building stops after compiling the plugin and doesn't proceed. // Run this test a number of times to try to catch any race conditions. for _ in 1...5 { - await XCTAssertAsyncThrowsError(try await SwiftPM.Build.execute(packagePath: packageDir)) { error in + await XCTAssertAsyncThrowsError(try await executeSwiftBuild(packageDir)) { error in guard case SwiftPMError.executionFailure(_, let stdout, _) = error else { return XCTFail("invalid error \(error)") } @@ -3766,3 +3785,39 @@ final class PackageCommandTests: CommandsTestCase { } } } + + +class PackageCommandNativeTests: PackageCommandTestCase { + + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .native + } + + override func testNoParameters() async throws { + try await super.testNoParameters() + } +} + +class PackageCommandSwiftBuildTests: PackageCommandTestCase { + + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .swiftbuild + } + + override func testNoParameters() async throws { + try await super.testNoParameters() + } + + override func testCommandPluginBuildingCallbacks() async throws { + throw XCTSkip("Test fails as plugins are not currenty supported") + } + override func testCommandPluginBuildTestability() async throws { + throw XCTSkip("Test fails as plugins are not currenty supported") + } + // override func testSeeAlso() async throws { + // throw XCTSkip("Test fails") + // } + // override func testVersion() async throws { + // throw XCTSkip("Test fails") + // } +} \ No newline at end of file diff --git a/Tests/CommandsTests/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index 34391d561b7..72d9f26d3fd 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -12,17 +12,30 @@ import Basics import Commands +import SPMBuildCore import _InternalTestSupport +import TSCTestSupport import XCTest import class Basics.AsyncProcess -final class RunCommandTests: CommandsTestCase { +class RunCommandTestCase: BuildSystemProviderTestCase { + override func setUpWithError() throws { + try XCTSkipIf(type(of: self) == RunCommandTestCase.self, "Pay no attention to the class behind the curtain.") + } + private func execute( _ args: [String] = [], + _ executable: String? = nil, packagePath: AbsolutePath? = nil ) async throws -> (stdout: String, stderr: String) { - return try await SwiftPM.Run.execute(args, packagePath: packagePath) + // return try await SwiftPM.Run.execute(args, packagePath: packagePath) + return try await executeSwiftRun( + packagePath, + nil, + extraArgs: args, + buildSystem: buildSystemProvider + ) } func testUsage() async throws { @@ -50,8 +63,10 @@ final class RunCommandTests: CommandsTestCase { #if !os(Windows) func testToolsetDebugger() async throws { try await fixture(name: "Miscellaneous/EchoExecutable") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Run.execute( - ["--toolset", "\(fixturePath)/toolset.json"], packagePath: fixturePath) + let (stdout, stderr) = try await execute( + ["--toolset", "\(fixturePath)/toolset.json"], + packagePath: fixturePath, + ) // We only expect tool's output on the stdout stream. XCTAssertMatch(stdout, .contains("\(fixturePath)/.build")) @@ -66,7 +81,7 @@ final class RunCommandTests: CommandsTestCase { func testUnknownProductAndArgumentPassing() async throws { try await fixture(name: "Miscellaneous/EchoExecutable") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Run.execute( + let (stdout, stderr) = try await execute( ["secho", "1", "--hello", "world"], packagePath: fixturePath) // We only expect tool's output on the stdout stream. @@ -124,6 +139,42 @@ final class RunCommandTests: CommandsTestCase { } } + private func _testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfully(configuration: TSCTestSupport.Configuration) async throws { + let expectedName: String + #if os(Windows) + expectedName = "Windows" + #else + #if os(macOS) + expectedName = "macOS" + #else + #if os(linux) + expectedName = "Linux" + #else + throw TestError.platformNotSupported + #endif + #endif + #endif + + try await fixture(name: "Miscellaneous/TargetConditionals/ExecutableTargetContainsPlatformConditional") { fixturePath in + let (stdout, _) = try await execute( + ["test"], + packagePath: fixturePath + ) + + XCTAssertMatch(stdout, .contains("Hello, world on \(expectedName)! OSplatform: \(expectedName)")) + + } + } + + func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { + try await self._testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfully(configuration: .Debug) + } + + func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { + try await self._testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfully(configuration: .Release) + } + + func testSwiftRunSIGINT() throws { try XCTSkipIfCI() try fixture(name: "Miscellaneous/SwiftRun") { fixturePath in @@ -219,3 +270,49 @@ final class RunCommandTests: CommandsTestCase { } } + +class RunCommandNativeTests: RunCommandTestCase { + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .native + } + + override func testUsage() async throws { + try await super.testUsage() + } +} + + +class RunCommandSwiftBuildTests: RunCommandTestCase { + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .swiftbuild + } + + override func testUsage() async throws { + try await super.testUsage() + } + + override func testMultipleExecutableAndExplicitExecutable() async throws { + try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") + } + + override func testUnknownProductAndArgumentPassing() async throws { + try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") + } + + override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { + try XCTSkip("Test fixture fails to build") + } + + override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { + try XCTSkip("Test fixture fails to build") + } + + override func testToolsetDebugger() async throws { + try XCTSkip("Test fixture fails to build") + } + + override func testUnreachableExecutable() async throws { + try XCTSkip("Need to investigate test failure") + } + +} diff --git a/Tests/CommandsTests/TestCommandTests.swift b/Tests/CommandsTests/TestCommandTests.swift index 6c1b253b9ba..070fb7531cf 100644 --- a/Tests/CommandsTests/TestCommandTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -12,18 +12,28 @@ import Basics import Commands +import SPMBuildCore import PackageModel import _InternalTestSupport import TSCTestSupport import XCTest -final class TestCommandTests: CommandsTestCase { +class TestCommandTestCase: CommandsBuildProviderTestCase { + override func setUpWithError() throws { + try XCTSkipIf(type(of: self) == TestCommandTestCase.self, "Pay no attention to the class behind the curtain.") + } + private func execute( _ args: [String], packagePath: AbsolutePath? = nil, throwIfCommandFails: Bool = true ) async throws -> (stdout: String, stderr: String) { - try await SwiftPM.Test.execute(args, packagePath: packagePath, throwIfCommandFails: throwIfCommandFails) + try await executeSwiftTest( + packagePath, + extraArgs: args, + throwIfCommandFails: throwIfCommandFails, + buildSystem: buildSystemProvider + ) } func testUsage() async throws { @@ -63,7 +73,7 @@ final class TestCommandTests: CommandsTestCase { #if !os(Windows) func testToolsetRunner() async throws { try await fixture(name: "Miscellaneous/EchoExecutable") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Test.execute( + let (stdout, stderr) = try await execute( ["--toolset", "\(fixturePath)/toolset.json"], packagePath: fixturePath) // We only expect tool's output on the stdout stream. @@ -138,21 +148,21 @@ final class TestCommandTests: CommandsTestCase { func testSwiftTestParallel() async throws { try await fixture(name: "Miscellaneous/ParallelTestsPkg") { fixturePath in // First try normal serial testing. - await XCTAssertThrowsCommandExecutionError(try await SwiftPM.Test.execute(packagePath: fixturePath)) { error in + await XCTAssertThrowsCommandExecutionError(try await execute([], packagePath: fixturePath)) { error in // in "swift test" test output goes to stdout XCTAssertMatch(error.stdout, .contains("Executed 2 tests")) XCTAssertNoMatch(error.stdout, .contains("[3/3]")) } // Try --no-parallel. - await XCTAssertThrowsCommandExecutionError(try await SwiftPM.Test.execute(["--no-parallel"], packagePath: fixturePath)) { error in + await XCTAssertThrowsCommandExecutionError(try await execute(["--no-parallel"], packagePath: fixturePath)) { error in // in "swift test" test output goes to stdout XCTAssertMatch(error.stdout, .contains("Executed 2 tests")) XCTAssertNoMatch(error.stdout, .contains("[3/3]")) } // Run tests in parallel. - await XCTAssertThrowsCommandExecutionError(try await SwiftPM.Test.execute(["--parallel"], packagePath: fixturePath)) { error in + await XCTAssertThrowsCommandExecutionError(try await execute(["--parallel"], packagePath: fixturePath)) { error in // in "swift test" test output goes to stdout XCTAssertMatch(error.stdout, .contains("testExample1")) XCTAssertMatch(error.stdout, .contains("testExample2")) @@ -165,7 +175,7 @@ final class TestCommandTests: CommandsTestCase { let xUnitOutput = fixturePath.appending("result.xml") // Run tests in parallel with verbose output. await XCTAssertThrowsCommandExecutionError( - try await SwiftPM.Test.execute(["--parallel", "--verbose", "--xunit-output", xUnitOutput.pathString], packagePath: fixturePath) + try await execute(["--parallel", "--verbose", "--xunit-output", xUnitOutput.pathString], packagePath: fixturePath) ) { error in // in "swift test" test output goes to stdout XCTAssertMatch(error.stdout, .contains("testExample1")) @@ -189,7 +199,7 @@ final class TestCommandTests: CommandsTestCase { try await fixture(name: "Miscellaneous/EmptyTestsPkg") { fixturePath in let xUnitOutput = fixturePath.appending("result.xml") // Run tests in parallel with verbose output. - _ = try await SwiftPM.Test.execute(["--parallel", "--verbose", "--xunit-output", xUnitOutput.pathString], packagePath: fixturePath).stdout + _ = try await execute(["--parallel", "--verbose", "--xunit-output", xUnitOutput.pathString], packagePath: fixturePath).stdout // Check the xUnit output. XCTAssertFileExists(xUnitOutput) @@ -378,7 +388,7 @@ final class TestCommandTests: CommandsTestCase { func testSwiftTestFilter() async throws { try await fixture(name: "Miscellaneous/SkipTests") { fixturePath in - let (stdout, _) = try await SwiftPM.Test.execute(["--filter", ".*1"], packagePath: fixturePath) + let (stdout, _) = try await execute(["--filter", ".*1"], packagePath: fixturePath) // in "swift test" test output goes to stdout XCTAssertMatch(stdout, .contains("testExample1")) XCTAssertNoMatch(stdout, .contains("testExample2")) @@ -387,7 +397,7 @@ final class TestCommandTests: CommandsTestCase { } try await fixture(name: "Miscellaneous/SkipTests") { fixturePath in - let (stdout, _) = try await SwiftPM.Test.execute(["--filter", "SomeTests", "--skip", ".*1", "--filter", "testExample3"], packagePath: fixturePath) + let (stdout, _) = try await execute(["--filter", "SomeTests", "--skip", ".*1", "--filter", "testExample3"], packagePath: fixturePath) // in "swift test" test output goes to stdout XCTAssertNoMatch(stdout, .contains("testExample1")) XCTAssertMatch(stdout, .contains("testExample2")) @@ -398,7 +408,7 @@ final class TestCommandTests: CommandsTestCase { func testSwiftTestSkip() async throws { try await fixture(name: "Miscellaneous/SkipTests") { fixturePath in - let (stdout, _) = try await SwiftPM.Test.execute(["--skip", "SomeTests"], packagePath: fixturePath) + let (stdout, _) = try await execute(["--skip", "SomeTests"], packagePath: fixturePath) // in "swift test" test output goes to stdout XCTAssertNoMatch(stdout, .contains("testExample1")) XCTAssertNoMatch(stdout, .contains("testExample2")) @@ -407,7 +417,7 @@ final class TestCommandTests: CommandsTestCase { } try await fixture(name: "Miscellaneous/SkipTests") { fixturePath in - let (stdout, _) = try await SwiftPM.Test.execute(["--filter", "ExampleTests", "--skip", ".*2", "--filter", "MoreTests", "--skip", "testExample3"], packagePath: fixturePath) + let (stdout, _) = try await execute(["--filter", "ExampleTests", "--skip", ".*2", "--filter", "MoreTests", "--skip", "testExample3"], packagePath: fixturePath) // in "swift test" test output goes to stdout XCTAssertMatch(stdout, .contains("testExample1")) XCTAssertNoMatch(stdout, .contains("testExample2")) @@ -416,7 +426,7 @@ final class TestCommandTests: CommandsTestCase { } try await fixture(name: "Miscellaneous/SkipTests") { fixturePath in - let (stdout, _) = try await SwiftPM.Test.execute(["--skip", "Tests"], packagePath: fixturePath) + let (stdout, _) = try await execute(["--skip", "Tests"], packagePath: fixturePath) // in "swift test" test output goes to stdout XCTAssertNoMatch(stdout, .contains("testExample1")) XCTAssertNoMatch(stdout, .contains("testExample2")) @@ -430,26 +440,26 @@ final class TestCommandTests: CommandsTestCase { #if canImport(Darwin) // should emit when LinuxMain is present try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (_, stderr) = try await SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + let (_, stderr) = try await execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) } // should emit when LinuxMain is not present try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in try localFileSystem.writeFileContents(fixturePath.appending(components: "Tests", SwiftModule.defaultTestEntryPointName), bytes: "fatalError(\"boom\")") - let (_, stderr) = try await SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + let (_, stderr) = try await execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) } #else // should emit when LinuxMain is present try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (_, stderr) = try await SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + let (_, stderr) = try await execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) XCTAssertMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) } // should not emit when LinuxMain is present try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in try localFileSystem.writeFileContents(fixturePath.appending(components: "Tests", SwiftModule.defaultTestEntryPointName), bytes: "fatalError(\"boom\")") - let (_, stderr) = try await SwiftPM.Test.execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) + let (_, stderr) = try await execute(["--enable-test-discovery"] + compilerDiagnosticFlags, packagePath: fixturePath) XCTAssertNoMatch(stderr, .contains("warning: '--enable-test-discovery' option is deprecated")) } #endif @@ -457,7 +467,7 @@ final class TestCommandTests: CommandsTestCase { func testList() async throws { try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (stdout, stderr) = try await SwiftPM.Test.execute(["list"], packagePath: fixturePath) + let (stdout, stderr) = try await execute(["list"], packagePath: fixturePath) // build was run XCTAssertMatch(stderr, .contains("Build complete!")) // getting the lists @@ -474,7 +484,7 @@ final class TestCommandTests: CommandsTestCase { } // list do { - let (stdout, stderr) = try await SwiftPM.Test.execute(["list"], packagePath: fixturePath) + let (stdout, stderr) = try await execute(["list"], packagePath: fixturePath) // build was run XCTAssertMatch(stderr, .contains("Build complete!")) // getting the lists @@ -492,7 +502,7 @@ final class TestCommandTests: CommandsTestCase { } // list while skipping build do { - let (stdout, stderr) = try await SwiftPM.Test.execute(["list", "--skip-build"], packagePath: fixturePath) + let (stdout, stderr) = try await execute(["list", "--skip-build"], packagePath: fixturePath) // build was not run XCTAssertNoMatch(stderr, .contains("Build complete!")) // getting the lists @@ -506,7 +516,7 @@ final class TestCommandTests: CommandsTestCase { func testListWithSkipBuildAndNoBuildArtifacts() async throws { try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in await XCTAssertThrowsCommandExecutionError( - try await SwiftPM.Test.execute(["list", "--skip-build"], packagePath: fixturePath) + try await execute(["list", "--skip-build"], packagePath: fixturePath) ) { error in XCTAssertMatch(error.stderr, .contains("Test build artifacts were not found in the build folder")) } @@ -523,7 +533,7 @@ final class TestCommandTests: CommandsTestCase { try await fixture(name: "Miscellaneous/TestDiscovery/SwiftTesting") { fixturePath in do { - let (stdout, _) = try await SwiftPM.Test.execute(["--enable-swift-testing", "--disable-xctest"], packagePath: fixturePath) + let (stdout, _) = try await execute(["--enable-swift-testing", "--disable-xctest"], packagePath: fixturePath) XCTAssertMatch(stdout, .contains(#"Test "SOME TEST FUNCTION" started"#)) } } @@ -539,7 +549,7 @@ final class TestCommandTests: CommandsTestCase { try await fixture(name: "Miscellaneous/TestDiscovery/SwiftTesting") { fixturePath in do { - let (stdout, _) = try await SwiftPM.Test.execute(["--enable-experimental-swift-testing", "--disable-xctest"], packagePath: fixturePath) + let (stdout, _) = try await execute(["--enable-experimental-swift-testing", "--disable-xctest"], packagePath: fixturePath) XCTAssertMatch(stdout, .contains(#"Test "SOME TEST FUNCTION" started"#)) } } @@ -549,7 +559,7 @@ final class TestCommandTests: CommandsTestCase { func testGeneratedMainIsConcurrencySafe_XCTest() async throws { let strictConcurrencyFlags = ["-Xswiftc", "-strict-concurrency=complete"] try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (_, stderr) = try await SwiftPM.Test.execute(strictConcurrencyFlags, packagePath: fixturePath) + let (_, stderr) = try await execute(strictConcurrencyFlags, packagePath: fixturePath) XCTAssertNoMatch(stderr, .contains("is not concurrency-safe")) } } @@ -559,7 +569,7 @@ final class TestCommandTests: CommandsTestCase { func testGeneratedMainIsExistentialAnyClean() async throws { let existentialAnyFlags = ["-Xswiftc", "-enable-upcoming-feature", "-Xswiftc", "ExistentialAny"] try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (_, stderr) = try await SwiftPM.Test.execute(existentialAnyFlags, packagePath: fixturePath) + let (_, stderr) = try await execute(existentialAnyFlags, packagePath: fixturePath) XCTAssertNoMatch(stderr, .contains("error: use of protocol")) } } @@ -577,7 +587,7 @@ final class TestCommandTests: CommandsTestCase { func testXCTestOnlyDoesNotLogAboutNoMatchingTests() async throws { try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in - let (_, stderr) = try await SwiftPM.Test.execute(["--disable-swift-testing"], packagePath: fixturePath) + let (_, stderr) = try await execute(["--disable-swift-testing"], packagePath: fixturePath) XCTAssertNoMatch(stderr, .contains("No matching test cases were run")) } } @@ -619,3 +629,50 @@ final class TestCommandTests: CommandsTestCase { } } + +class TestCommandNativeTests: TestCommandTestCase { + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .native + } + + override func testUsage() async throws { + try await super.testUsage() + } +} + + +class TestCommandSwiftBuildTests: TestCommandTestCase { + override open var buildSystemProvider: BuildSystemProvider.Kind { + return .swiftbuild + } + + override func testUsage() async throws { + try await super.testUsage() + } + + override func testList() async throws { + try XCTSkip("Test currently fails due to 'error: build failed'") + } + + override func testEnableTestDiscoveryDeprecation() async throws { + try XCTSkip("Test currently fails due to 'error: build failed'") + } + + override func testEnableDisableTestability() async throws { + try XCTSkip("Test currently fails due to 'error: build failed'") + } + + override func testToolsetRunner() async throws { + try XCTSkip("Test currently fails, as some assertions are not met") + } + + override func testWithReleaseConfiguration() async throws { + try XCTSkip("Test currently fails with 'error: toolchain is invalid: could not find CLI tool `swiftpm-testing-helper` at any of these directories: [..., ...]'") + } + + override func testXCTestOnlyDoesNotLogAboutNoMatchingTests() async throws { + try XCTSkip("Test currently fails assertion as the there is a different error message 'error: no tests found; create a target in the 'Tests' directory'") + } + + +} From f3625a80b367f42b637f805fb9d9592e9d22b42a Mon Sep 17 00:00:00 2001 From: Sam Khouri Date: Wed, 5 Feb 2025 22:38:22 -0500 Subject: [PATCH 2/4] Attempt solution at a custom XFail for XCTest.. --- Sources/_InternalTestSupport/Commands.swift | 30 ++++++++++ Tests/BuildTests/BuildPlanTests.swift | 29 +++++++-- Tests/CommandsTests/RunCommandTests.swift | 65 +++++++++++++++------ 3 files changed, 100 insertions(+), 24 deletions(-) diff --git a/Sources/_InternalTestSupport/Commands.swift b/Sources/_InternalTestSupport/Commands.swift index bb69f7b84dd..ee71de03369 100644 --- a/Sources/_InternalTestSupport/Commands.swift +++ b/Sources/_InternalTestSupport/Commands.swift @@ -1,8 +1,38 @@ import SPMBuildCore import XCTest +public struct XFailCaseName { + let testName: String + let reason: String + + public init(_ testName: String, because reason: String) { + self.testName = testName + self.reason = reason + } +} open class BuildSystemProviderTestCase: XCTestCase { open var buildSystemProvider: BuildSystemProvider.Kind { fatalError("\(self) does not implement \(#function)") } + + open var xFailTestCaseNames: [XFailCaseName] { + return [] + } + + override open func recordFailure(withDescription description: String, inFile filePath: String, atLine lineNumber: Int, expected: Bool) { + // Get current test name: + print("--->> In recordFailure: Test name is >>>\(self.name)<<<") + + if self.xFailTestCaseNames.map({ item in item.testName }).contains(self.name) { + // do nothing + print("--->> In recordFailure: Test name is >>>\(self.name)<<< is expected to fail, so mark as passed!!") + } else { + super.recordFailure( + withDescription: description, + inFile: filePath, + atLine: lineNumber, + expected: expected + ) + } + } } diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 49dac5d3f6e..f3801f5a9c6 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -6880,17 +6880,34 @@ class BuildPlanSwiftBuildTests: BuildPlanTestCase { return .swiftbuild } + override open var xFailTestCaseNames: [XFailCaseName] { + return [ + XFailCaseName( + String(describing: testDuplicateProductNamesWithNonDefaultLibsThrowError.self), + because: "This test is not expected to fail.. it should pass.", + ), + XFailCaseName( + String(describing: testTargetsWithPackageAccess.self), + because: "Skip until swift build system can support this case", + ), + // XFailCaseName( + // String(describing: testTestModule.self), + // because: "Skip until swift build system can support this case." + // ) + ] + } + override func testDuplicateProductNamesWithNonDefaultLibsThrowError() async throws { try await super.testDuplicateProductNamesWithNonDefaultLibsThrowError() } - override func testTargetsWithPackageAccess() async throws { - throw XCTSkip("Skip until swift build system can support this case.") - } + // override func testTargetsWithPackageAccess() async throws { + // throw XCTSkip("Skip until swift build system can support this case.") + // } - override func testTestModule() async throws { - throw XCTSkip("Skip until swift build system can support this case.") - } + // override func testTestModule() async throws { + // throw XCTSkip("Skip until swift build system can support this case.") + // } override func testPackageNameFlag() async throws { #if os(Windows) diff --git a/Tests/CommandsTests/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index 72d9f26d3fd..38b38a4b055 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -19,7 +19,7 @@ import XCTest import class Basics.AsyncProcess -class RunCommandTestCase: BuildSystemProviderTestCase { +class RunCommandTestCase: CommandsBuildProviderTestCase { override func setUpWithError() throws { try XCTSkipIf(type(of: self) == RunCommandTestCase.self, "Pay no attention to the class behind the curtain.") } @@ -291,28 +291,57 @@ class RunCommandSwiftBuildTests: RunCommandTestCase { try await super.testUsage() } - override func testMultipleExecutableAndExplicitExecutable() async throws { - try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") + override open var xFailTestCaseNames: [XFailCaseName] { + return [ + XFailCaseName( + String(describing: testMultipleExecutableAndExplicitExecutable.self), + because: "https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal", + ), + XFailCaseName( + String(describing: testUnknownProductAndArgumentPassing.self), + because: "https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal", + ), + XFailCaseName( + String(describing: testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig.self), + because: "Test fixture fails to build", + ), + XFailCaseName( + String(describing: testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig.self), + because: "Test fixture fails to build", + ), + XFailCaseName( + String(describing: testToolsetDebugger.self), + because: "Test fixture fails to build", + ), + XFailCaseName( + String(describing: testUnreachableExecutable.self), + because: "Need to investigate test failure", + ), + ] } - override func testUnknownProductAndArgumentPassing() async throws { - try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") - } + // override func testMultipleExecutableAndExplicitExecutable() async throws { + // try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") + // } - override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { - try XCTSkip("Test fixture fails to build") - } + // override func testUnknownProductAndArgumentPassing() async throws { + // try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") + // } - override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { - try XCTSkip("Test fixture fails to build") - } + // override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { + // try XCTSkip("Test fixture fails to build") + // } - override func testToolsetDebugger() async throws { - try XCTSkip("Test fixture fails to build") - } + // override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { + // try XCTSkip("Test fixture fails to build") + // } - override func testUnreachableExecutable() async throws { - try XCTSkip("Need to investigate test failure") - } + // override func testToolsetDebugger() async throws { + // try XCTSkip("Test fixture fails to build") + // } + + // override func testUnreachableExecutable() async throws { + // try XCTSkip("Need to investigate test failure") + // } } From b14a7e17af8f8928f74bff6af30f2310c7d384cd Mon Sep 17 00:00:00 2001 From: Sam Khouri Date: Mon, 3 Mar 2025 10:45:31 -0500 Subject: [PATCH 3/4] Revert "Attempt solution at a custom XFail for XCTest.." This reverts commit 3e06c8f497b33b24aefc18290d9226dea01525e6. --- Sources/_InternalTestSupport/Commands.swift | 30 ---------- Tests/BuildTests/BuildPlanTests.swift | 29 ++------- Tests/CommandsTests/RunCommandTests.swift | 65 ++++++--------------- 3 files changed, 24 insertions(+), 100 deletions(-) diff --git a/Sources/_InternalTestSupport/Commands.swift b/Sources/_InternalTestSupport/Commands.swift index ee71de03369..bb69f7b84dd 100644 --- a/Sources/_InternalTestSupport/Commands.swift +++ b/Sources/_InternalTestSupport/Commands.swift @@ -1,38 +1,8 @@ import SPMBuildCore import XCTest -public struct XFailCaseName { - let testName: String - let reason: String - - public init(_ testName: String, because reason: String) { - self.testName = testName - self.reason = reason - } -} open class BuildSystemProviderTestCase: XCTestCase { open var buildSystemProvider: BuildSystemProvider.Kind { fatalError("\(self) does not implement \(#function)") } - - open var xFailTestCaseNames: [XFailCaseName] { - return [] - } - - override open func recordFailure(withDescription description: String, inFile filePath: String, atLine lineNumber: Int, expected: Bool) { - // Get current test name: - print("--->> In recordFailure: Test name is >>>\(self.name)<<<") - - if self.xFailTestCaseNames.map({ item in item.testName }).contains(self.name) { - // do nothing - print("--->> In recordFailure: Test name is >>>\(self.name)<<< is expected to fail, so mark as passed!!") - } else { - super.recordFailure( - withDescription: description, - inFile: filePath, - atLine: lineNumber, - expected: expected - ) - } - } } diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index f3801f5a9c6..49dac5d3f6e 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -6880,34 +6880,17 @@ class BuildPlanSwiftBuildTests: BuildPlanTestCase { return .swiftbuild } - override open var xFailTestCaseNames: [XFailCaseName] { - return [ - XFailCaseName( - String(describing: testDuplicateProductNamesWithNonDefaultLibsThrowError.self), - because: "This test is not expected to fail.. it should pass.", - ), - XFailCaseName( - String(describing: testTargetsWithPackageAccess.self), - because: "Skip until swift build system can support this case", - ), - // XFailCaseName( - // String(describing: testTestModule.self), - // because: "Skip until swift build system can support this case." - // ) - ] - } - override func testDuplicateProductNamesWithNonDefaultLibsThrowError() async throws { try await super.testDuplicateProductNamesWithNonDefaultLibsThrowError() } - // override func testTargetsWithPackageAccess() async throws { - // throw XCTSkip("Skip until swift build system can support this case.") - // } + override func testTargetsWithPackageAccess() async throws { + throw XCTSkip("Skip until swift build system can support this case.") + } - // override func testTestModule() async throws { - // throw XCTSkip("Skip until swift build system can support this case.") - // } + override func testTestModule() async throws { + throw XCTSkip("Skip until swift build system can support this case.") + } override func testPackageNameFlag() async throws { #if os(Windows) diff --git a/Tests/CommandsTests/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index 38b38a4b055..72d9f26d3fd 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -19,7 +19,7 @@ import XCTest import class Basics.AsyncProcess -class RunCommandTestCase: CommandsBuildProviderTestCase { +class RunCommandTestCase: BuildSystemProviderTestCase { override func setUpWithError() throws { try XCTSkipIf(type(of: self) == RunCommandTestCase.self, "Pay no attention to the class behind the curtain.") } @@ -291,57 +291,28 @@ class RunCommandSwiftBuildTests: RunCommandTestCase { try await super.testUsage() } - override open var xFailTestCaseNames: [XFailCaseName] { - return [ - XFailCaseName( - String(describing: testMultipleExecutableAndExplicitExecutable.self), - because: "https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal", - ), - XFailCaseName( - String(describing: testUnknownProductAndArgumentPassing.self), - because: "https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal", - ), - XFailCaseName( - String(describing: testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig.self), - because: "Test fixture fails to build", - ), - XFailCaseName( - String(describing: testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig.self), - because: "Test fixture fails to build", - ), - XFailCaseName( - String(describing: testToolsetDebugger.self), - because: "Test fixture fails to build", - ), - XFailCaseName( - String(describing: testUnreachableExecutable.self), - because: "Need to investigate test failure", - ), - ] + override func testMultipleExecutableAndExplicitExecutable() async throws { + try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") } - // override func testMultipleExecutableAndExplicitExecutable() async throws { - // try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") - // } - - // override func testUnknownProductAndArgumentPassing() async throws { - // try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") - // } + override func testUnknownProductAndArgumentPassing() async throws { + try XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8279: Swift run using Swift Build does not output executable content to the terminal") + } - // override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { - // try XCTSkip("Test fixture fails to build") - // } + override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInDebugConfig() async throws { + try XCTSkip("Test fixture fails to build") + } - // override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { - // try XCTSkip("Test fixture fails to build") - // } + override func testPackageWithExcutableTargetsContainsPlatformConditionalsBuildsSuccessfullyInReleaseConfig() async throws { + try XCTSkip("Test fixture fails to build") + } - // override func testToolsetDebugger() async throws { - // try XCTSkip("Test fixture fails to build") - // } + override func testToolsetDebugger() async throws { + try XCTSkip("Test fixture fails to build") + } - // override func testUnreachableExecutable() async throws { - // try XCTSkip("Need to investigate test failure") - // } + override func testUnreachableExecutable() async throws { + try XCTSkip("Need to investigate test failure") + } } From e9a2a9cc061f244b9f3f6bb5a3fa18fc653d43f5 Mon Sep 17 00:00:00 2001 From: Sam Khouri Date: Mon, 3 Mar 2025 10:59:58 -0500 Subject: [PATCH 4/4] Have RunCommandTests inherit from CommandsBuildProviderTestCase --- Tests/CommandsTests/RunCommandTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/CommandsTests/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index 72d9f26d3fd..fd8d7f97e01 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -19,7 +19,7 @@ import XCTest import class Basics.AsyncProcess -class RunCommandTestCase: BuildSystemProviderTestCase { +class RunCommandTestCase: CommandsBuildProviderTestCase { override func setUpWithError() throws { try XCTSkipIf(type(of: self) == RunCommandTestCase.self, "Pay no attention to the class behind the curtain.") }