From 925baa595e4a464118b0744548b5a09e0fe77d54 Mon Sep 17 00:00:00 2001 From: Sam Khouri Date: Tue, 4 Feb 2025 09:33:29 -0500 Subject: [PATCH] Tests: augment command tests --- .../.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 + Sources/_InternalTestSupport/misc.swift | 4 + Tests/CommandsTests/BuildCommandTests.swift | 159 +++++++++++++++++- Tests/CommandsTests/CommandsTestCase.swift | 18 ++ Tests/CommandsTests/RunCommandTests.swift | 37 ++++ 13 files changed, 347 insertions(+), 9 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/Sources/_InternalTestSupport/misc.swift b/Sources/_InternalTestSupport/misc.swift index 2c08c1a6770..1112f648e53 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, diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index da30851725e..6fdf0c223aa 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 } @@ -165,7 +184,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( @@ -199,7 +218,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( @@ -226,7 +254,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( @@ -416,7 +448,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 defaultOutput = try await execute(["--build-system", buildsystem, "-c", "debug", "-v"], packagePath: fixturePath).stdout + let defaultOutput = try await execute(["-c", "debug", "-v"], packagePath: fixturePath,).stdout // Look for certain things in the output from XCBuild. XCTAssertMatch( @@ -535,6 +567,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 { @@ -687,13 +740,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) @@ -737,3 +806,75 @@ 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 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 b49fe7561a8..6e168123170 100644 --- a/Tests/CommandsTests/CommandsTestCase.swift +++ b/Tests/CommandsTests/CommandsTestCase.swift @@ -12,6 +12,7 @@ import Basics import XCTest +import _InternalTestSupport class CommandsTestCase: XCTestCase { @@ -30,3 +31,20 @@ 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 + + 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/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index b15dd0bd216..d5e62dabb6b 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -13,6 +13,7 @@ import Basics import Commands import _InternalTestSupport +import TSCTestSupport import XCTest import class Basics.AsyncProcess @@ -118,6 +119,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