From b2f3c34d4691841fb54792f1f9db5ce5578c5cd7 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 23 Aug 2024 13:50:52 -0400 Subject: [PATCH 1/2] [6.0] Update package templates to use Swift Testing in the toolchain rather than as a package dependency. (#7872) This PR updates the package templates used by `swift package init` to use Swift Testing by default where possible, and to not add a package dependency on the Swift Testing open-source repository since it is now building in the toolchain. Resolves rdar://128272585. --- .../TestDiscovery/SwiftTesting/Package.swift | 13 +---- Sources/Commands/PackageCommands/Init.swift | 12 ++-- Sources/PackageModelSyntax/AddTarget.swift | 51 +---------------- Sources/SPMTestSupport/misc.swift | 2 +- Sources/Workspace/InitPackage.swift | 51 +++++++---------- Tests/CommandsTests/TestCommandTests.swift | 18 ++++++ .../ManifestEditTests.swift | 16 ++---- Tests/WorkspaceTests/InitTests.swift | 57 ++++++++----------- 8 files changed, 78 insertions(+), 142 deletions(-) diff --git a/Fixtures/Miscellaneous/TestDiscovery/SwiftTesting/Package.swift b/Fixtures/Miscellaneous/TestDiscovery/SwiftTesting/Package.swift index 2588f440a55..927603bf003 100644 --- a/Fixtures/Miscellaneous/TestDiscovery/SwiftTesting/Package.swift +++ b/Fixtures/Miscellaneous/TestDiscovery/SwiftTesting/Package.swift @@ -1,18 +1,9 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.0 import PackageDescription let package = Package( name: "SwiftTesting", - platforms: [ - .macOS(.v13), .iOS(.v16), .watchOS(.v9), .tvOS(.v16), .visionOS(.v1) - ], - dependencies: [ - .package(url: "https://github.com/apple/swift-testing.git", branch: "main"), - ], targets: [ - .testTarget( - name: "SwiftTestingTests", - dependencies: [.product(name: "Testing", package: "swift-testing"),] - ), + .testTarget(name: "SwiftTestingTests"), ] ) diff --git a/Sources/Commands/PackageCommands/Init.swift b/Sources/Commands/PackageCommands/Init.swift index 38463a1f79b..6f40bcab180 100644 --- a/Sources/Commands/PackageCommands/Init.swift +++ b/Sources/Commands/PackageCommands/Init.swift @@ -56,14 +56,16 @@ extension SwiftPackageCommand { let packageName = self.packageName ?? cwd.basename - // Which testing libraries should be used? XCTest is on by default, - // but Swift Testing must remain off by default until it is present - // in the Swift toolchain. + // Testing is on by default, with XCTest only enabled explicitly. + // For macros this is reversed, since we don't support testing + // macros with Swift Testing yet. var supportedTestingLibraries = Set() - if testLibraryOptions.isEnabled(.xctest) { + if testLibraryOptions.isExplicitlyEnabled(.xctest) || + (initMode == .macro && testLibraryOptions.isEnabled(.xctest)) { supportedTestingLibraries.insert(.xctest) } - if testLibraryOptions.isExplicitlyEnabled(.swiftTesting) { + if testLibraryOptions.isExplicitlyEnabled(.swiftTesting) || + (initMode != .macro && testLibraryOptions.isEnabled(.swiftTesting)) { supportedTestingLibraries.insert(.swiftTesting) } diff --git a/Sources/PackageModelSyntax/AddTarget.swift b/Sources/PackageModelSyntax/AddTarget.swift index b32f705e990..53bdb3b5271 100644 --- a/Sources/PackageModelSyntax/AddTarget.swift +++ b/Sources/PackageModelSyntax/AddTarget.swift @@ -84,11 +84,6 @@ public struct AddTarget { // SwiftSyntax. target.dependencies.append(contentsOf: macroTargetDependencies) - case .test where configuration.testHarness == .swiftTesting: - // Testing targets using swift-testing need to depend on - // SwiftTesting from the swift-testing package. - target.dependencies.append(contentsOf: swiftTestingTestTargetDependencies) - default: break; } @@ -163,17 +158,6 @@ public struct AddTarget { } } - case .test where configuration.testHarness == .swiftTesting: - if !manifest.description.contains("swift-testing") { - newPackageCall = try AddPackageDependency - .addPackageDependencyLocal( - .swiftTesting( - configuration: installedSwiftPMConfiguration - ), - to: newPackageCall - ) - } - default: break; } @@ -212,8 +196,7 @@ public struct AddTarget { importModuleNames.append("XCTest") case .swiftTesting: - // Import is handled by the added dependency. - break + importModuleNames.append("Testing") } } @@ -380,35 +363,3 @@ fileprivate extension PackageDependency { ) } } - -/// The set of dependencies we need to introduce to a newly-created macro -/// target. -fileprivate let swiftTestingTestTargetDependencies: [TargetDescription.Dependency] = [ - .product(name: "Testing", package: "swift-testing"), -] - - -/// The package dependency for swift-testing, for use in test files. -fileprivate extension PackageDependency { - /// Source control URL for the swift-syntax package. - static var swiftTestingURL: SourceControlURL { - "https://github.com/apple/swift-testing.git" - } - - /// Package dependency on the swift-testing package. - static func swiftTesting( - configuration: InstalledSwiftPMConfiguration - ) -> PackageDependency { - let swiftTestingVersionDefault = - configuration.swiftTestingVersionForTestTemplate - let swiftTestingVersion = Version(swiftTestingVersionDefault.description)! - - return .sourceControl( - identity: PackageIdentity(url: swiftTestingURL), - nameForTargetDependencyResolutionOnly: nil, - location: .remote(swiftTestingURL), - requirement: .range(.upToNextMajor(from: swiftTestingVersion)), - productFilter: .everything - ) - } -} diff --git a/Sources/SPMTestSupport/misc.swift b/Sources/SPMTestSupport/misc.swift index f003bf21c93..d88da13bb32 100644 --- a/Sources/SPMTestSupport/misc.swift +++ b/Sources/SPMTestSupport/misc.swift @@ -474,7 +474,7 @@ extension InitPackage { public convenience init( name: String, packageType: PackageType, - supportedTestingLibraries: Set = [.xctest], + supportedTestingLibraries: Set = [.swiftTesting], destinationPath: AbsolutePath, fileSystem: FileSystem ) throws { diff --git a/Sources/Workspace/InitPackage.swift b/Sources/Workspace/InitPackage.swift index 866fb292835..6d28d67ed44 100644 --- a/Sources/Workspace/InitPackage.swift +++ b/Sources/Workspace/InitPackage.swift @@ -36,7 +36,7 @@ public final class InitPackage { public init( packageType: PackageType, - supportedTestingLibraries: Set = [.xctest], + supportedTestingLibraries: Set, platforms: [SupportedPlatform] = [] ) { self.packageType = packageType @@ -186,8 +186,8 @@ public final class InitPackage { var platforms = options.platforms - // Macros and swift-testing require macOS 10.15, iOS 13, etc. - if packageType == .macro || options.supportedTestingLibraries.contains(.swiftTesting) { + // Macros require macOS 10.15, iOS 13, etc. + if packageType == .macro { func addIfMissing(_ newPlatform: SupportedPlatform) { if platforms.contains(where: { platform in platform.platform == newPlatform.platform @@ -275,9 +275,6 @@ public final class InitPackage { } else if packageType == .macro { dependencies.append(#".package(url: "https://github.com/swiftlang/swift-syntax.git", from: "\#(self.installedSwiftPMConfiguration.swiftSyntaxVersionForMacroTemplate.description)")"#) } - if options.supportedTestingLibraries.contains(.swiftTesting) { - dependencies.append(#".package(url: "https://github.com/apple/swift-testing.git", from: "0.11.0")"#) - } if !dependencies.isEmpty { let dependencies = dependencies.map { dependency in " \(dependency)," @@ -384,17 +381,7 @@ public final class InitPackage { """ } else { let testTarget: String - if options.supportedTestingLibraries.contains(.swiftTesting) { - testTarget = """ - .testTarget( - name: "\(pkgname)Tests", - dependencies: [ - "\(pkgname)", - .product(name: "Testing", package: "swift-testing"), - ] - ), - """ - } else if options.supportedTestingLibraries.contains(.xctest) { + if !options.supportedTestingLibraries.isEmpty { testTarget = """ .testTarget( name: "\(pkgname)Tests", @@ -687,6 +674,10 @@ public final class InitPackage { private func writeLibraryTestsFile(_ path: AbsolutePath) throws { var content = "" + // XCTest is only added if it was explicitly asked for, so add tests + // for it *and* Testing if it is enabled (or just XCTest if Testing + // is explicitly disabled). + if options.supportedTestingLibraries.contains(.swiftTesting) { content += "import Testing\n" } @@ -695,20 +686,18 @@ public final class InitPackage { } content += "@testable import \(moduleName)\n" - // Prefer swift-testing if specified, otherwise XCTest. If both are - // specified, the developer is free to write tests using both - // libraries, but we still only want to present a single library's - // example tests. + if options.supportedTestingLibraries.contains(.swiftTesting) { content += """ - @Test func example() throws { - // swift-testing Documentation - // https://swiftpackageindex.com/apple/swift-testing/main/documentation/testing + @Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. } """ - } else if options.supportedTestingLibraries.contains(.xctest) { + } + + if options.supportedTestingLibraries.contains(.xctest) { content += """ final class \(moduleName)Tests: XCTestCase { @@ -761,13 +750,15 @@ public final class InitPackage { """## - // Prefer swift-testing if specified, otherwise XCTest. If both are - // specified, the developer is free to write tests using both - // libraries, but we still only want to present a single library's - // example tests. + + // XCTest is only added if it was explicitly asked for, so add tests + // for it *and* Testing if it is enabled. + if options.supportedTestingLibraries.contains(.swiftTesting) { // FIXME: https://github.com/swiftlang/swift-syntax/issues/2400 - } else if options.supportedTestingLibraries.contains(.xctest) { + } + + if options.supportedTestingLibraries.contains(.xctest) { content += ##""" final class \##(moduleName)Tests: XCTestCase { func testMacro() throws { diff --git a/Tests/CommandsTests/TestCommandTests.swift b/Tests/CommandsTests/TestCommandTests.swift index e419671247c..39889a70a32 100644 --- a/Tests/CommandsTests/TestCommandTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -279,10 +279,28 @@ final class TestCommandTests: CommandsTestCase { } func testBasicSwiftTestingIntegration() async throws { +#if !canImport(Testing) try XCTSkipUnless( nil != Environment.current["SWIFT_PM_SWIFT_TESTING_TESTS_ENABLED"], "Skipping \(#function) because swift-testing tests are not explicitly enabled" ) +#endif + + try await fixture(name: "Miscellaneous/TestDiscovery/SwiftTesting") { fixturePath in + do { + let (stdout, _) = try await SwiftPM.Test.execute(["--enable-swift-testing", "--disable-xctest"], packagePath: fixturePath) + XCTAssertMatch(stdout, .contains(#"Test "SOME TEST FUNCTION" started"#)) + } + } + } + + func testBasicSwiftTestingIntegration_ExperimentalFlag() async throws { +#if !canImport(Testing) + try XCTSkipUnless( + nil != Environment.current["SWIFT_PM_SWIFT_TESTING_TESTS_ENABLED"], + "Skipping \(#function) because swift-testing tests are not explicitly enabled" + ) +#endif try await fixture(name: "Miscellaneous/TestDiscovery/SwiftTesting") { fixturePath in do { diff --git a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift index b2c6254ce67..1294e2b1792 100644 --- a/Tests/PackageModelSyntaxTests/ManifestEditTests.swift +++ b/Tests/PackageModelSyntaxTests/ManifestEditTests.swift @@ -575,14 +575,8 @@ class ManifestEditTests: XCTestCase { // swift-tools-version: 5.5 let package = Package( name: "packages", - dependencies: [ - .package(url: "https://github.com/apple/swift-testing.git", from: "0.8.0"), - ], targets: [ - .testTarget( - name: "MyTest", - dependencies: [ .product(name: "Testing", package: "swift-testing") ] - ), + .testTarget(name: "MyTest"), ] ) """, @@ -618,7 +612,7 @@ class ManifestEditTests: XCTestCase { let package = Package( name: "packages", dependencies: [ - .package(url: "https://github.com/apple/swift-testing.git", from: "0.8.0"), + .package(url: "https://github.com/swiftlang/swift-example.git", from: "1.2.3"), ], targets: [ .testTarget( @@ -632,20 +626,20 @@ class ManifestEditTests: XCTestCase { let package = Package( name: "packages", dependencies: [ - .package(url: "https://github.com/apple/swift-testing.git", from: "0.8.0"), + .package(url: "https://github.com/swiftlang/swift-example.git", from: "1.2.3"), ], targets: [ .testTarget( name: "MyTest", dependencies: [ - .product(name: "Testing", package: "swift-testing"), + .product(name: "SomethingOrOther", package: "swift-example"), ] ), ] ) """) { manifest in try AddTargetDependency.addTargetDependency( - .product(name: "Testing", package: "swift-testing"), + .product(name: "SomethingOrOther", package: "swift-example"), targetName: "MyTest", to: manifest ) diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 96958148cfc..2ee9287a702 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -113,6 +113,7 @@ final class InitTests: XCTestCase { let initPackage = try InitPackage( name: name, packageType: .library, + supportedTestingLibraries: [.xctest], destinationPath: path, fileSystem: localFileSystem ) @@ -153,8 +154,8 @@ final class InitTests: XCTestCase { } } - func testInitPackageLibraryWithSwiftTestingOnly() throws { - try testWithTemporaryDirectory { tmpPath in + func testInitPackageLibraryWithSwiftTestingOnly() async throws { + try await testWithTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") let name = path.basename @@ -173,31 +174,25 @@ final class InitTests: XCTestCase { // Verify basic file system content that we expect in the package let manifest = path.appending("Package.swift") XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .contains(#".macOS(.v10_15)"#)) - XCTAssertMatch(manifestContents, .contains(#".iOS(.v13)"#)) - XCTAssertMatch(manifestContents, .contains(#".tvOS(.v13)"#)) - XCTAssertMatch(manifestContents, .contains(#".watchOS(.v6)"#)) - XCTAssertMatch(manifestContents, .contains(#".macCatalyst(.v13)"#)) - XCTAssertMatch(manifestContents, .contains(#"swift-testing.git", from: "0.11.0""#)) - XCTAssertMatch(manifestContents, .contains(#".product(name: "Testing", package: "swift-testing")"#)) let testFile = path.appending("Tests").appending("FooTests").appending("FooTests.swift") let testFileContents: String = try localFileSystem.readFileContents(testFile) XCTAssertMatch(testFileContents, .contains(#"import Testing"#)) XCTAssertNoMatch(testFileContents, .contains(#"import XCTest"#)) - XCTAssertMatch(testFileContents, .contains(#"@Test func example() throws"#)) + XCTAssertMatch(testFileContents, .contains(#"@Test func example() async throws"#)) XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) - // Try building it -- DISABLED because we cannot pull the swift-testing repository from CI. -// XCTAssertBuilds(path) -// let triple = try UserToolchain.default.targetTriple -// XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) +#if canImport(Testing) + // Try building it + await XCTAssertBuilds(path) + let triple = try UserToolchain.default.targetTriple + XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) +#endif } } - func testInitPackageLibraryWithBothSwiftTestingAndXCTest() throws { - try testWithTemporaryDirectory { tmpPath in + func testInitPackageLibraryWithBothSwiftTestingAndXCTest() async throws { + try await testWithTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") let name = path.basename @@ -216,26 +211,20 @@ final class InitTests: XCTestCase { // Verify basic file system content that we expect in the package let manifest = path.appending("Package.swift") XCTAssertFileExists(manifest) - let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .contains(#".macOS(.v10_15)"#)) - XCTAssertMatch(manifestContents, .contains(#".iOS(.v13)"#)) - XCTAssertMatch(manifestContents, .contains(#".tvOS(.v13)"#)) - XCTAssertMatch(manifestContents, .contains(#".watchOS(.v6)"#)) - XCTAssertMatch(manifestContents, .contains(#".macCatalyst(.v13)"#)) - XCTAssertMatch(manifestContents, .contains(#"swift-testing.git", from: "0.11.0""#)) - XCTAssertMatch(manifestContents, .contains(#".product(name: "Testing", package: "swift-testing")"#)) let testFile = path.appending("Tests").appending("FooTests").appending("FooTests.swift") let testFileContents: String = try localFileSystem.readFileContents(testFile) XCTAssertMatch(testFileContents, .contains(#"import Testing"#)) XCTAssertMatch(testFileContents, .contains(#"import XCTest"#)) - XCTAssertMatch(testFileContents, .contains(#"@Test func example() throws"#)) - XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) + XCTAssertMatch(testFileContents, .contains(#"@Test func example() async throws"#)) + XCTAssertMatch(testFileContents, .contains("func testExample() throws")) - // Try building it -- DISABLED because we cannot pull the swift-testing repository from CI. - // XCTAssertBuilds(path) - // let triple = try UserToolchain.default.targetTriple - // XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) +#if canImport(Testing) + // Try building it + await XCTAssertBuilds(path) + let triple = try UserToolchain.default.targetTriple + XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) +#endif } } @@ -262,16 +251,16 @@ final class InitTests: XCTestCase { let manifest = path.appending("Package.swift") XCTAssertFileExists(manifest) let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertNoMatch(manifestContents, .contains(#"swift-testing.git", from: "0.11.0""#)) - XCTAssertNoMatch(manifestContents, .contains(#".product(name: "Testing", package: "swift-testing")"#)) XCTAssertNoMatch(manifestContents, .contains(#".testTarget"#)) XCTAssertNoSuchPath(path.appending("Tests")) +#if canImport(Testing) // Try building it await XCTAssertBuilds(path) let triple = try UserToolchain.default.targetTriple XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) +#endif } } @@ -399,7 +388,7 @@ final class InitTests: XCTestCase { func testPlatforms() throws { try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in - var options = InitPackage.InitPackageOptions(packageType: .library) + var options = InitPackage.InitPackageOptions(packageType: .library, supportedTestingLibraries: []) options.platforms = [ .init(platform: .macOS, version: PlatformVersion("10.15")), .init(platform: .iOS, version: PlatformVersion("12")), From 1d6aeac6def2595c52703fd62158e5b92e41a654 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Sat, 24 Aug 2024 11:46:25 -0700 Subject: [PATCH 2/2] Temporarily disable new Swift Testing tests `canImport(Testing)` currently fails on the full toolchain test jobs due to various issues when building for both x86 and arm64. Temporarily remove this check until we can fix the underlying problems there. --- Tests/CommandsTests/TestCommandTests.swift | 4 ++-- Tests/WorkspaceTests/InitTests.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/CommandsTests/TestCommandTests.swift b/Tests/CommandsTests/TestCommandTests.swift index 39889a70a32..f861efa89fa 100644 --- a/Tests/CommandsTests/TestCommandTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -279,7 +279,7 @@ final class TestCommandTests: CommandsTestCase { } func testBasicSwiftTestingIntegration() async throws { -#if !canImport(Testing) +#if !canImport(TestingDisabled) try XCTSkipUnless( nil != Environment.current["SWIFT_PM_SWIFT_TESTING_TESTS_ENABLED"], "Skipping \(#function) because swift-testing tests are not explicitly enabled" @@ -295,7 +295,7 @@ final class TestCommandTests: CommandsTestCase { } func testBasicSwiftTestingIntegration_ExperimentalFlag() async throws { -#if !canImport(Testing) +#if !canImport(TestingDisabled) try XCTSkipUnless( nil != Environment.current["SWIFT_PM_SWIFT_TESTING_TESTS_ENABLED"], "Skipping \(#function) because swift-testing tests are not explicitly enabled" diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 2ee9287a702..731b800dc20 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -182,7 +182,7 @@ final class InitTests: XCTestCase { XCTAssertMatch(testFileContents, .contains(#"@Test func example() async throws"#)) XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) -#if canImport(Testing) +#if canImport(TestingDisabled) // Try building it await XCTAssertBuilds(path) let triple = try UserToolchain.default.targetTriple @@ -219,7 +219,7 @@ final class InitTests: XCTestCase { XCTAssertMatch(testFileContents, .contains(#"@Test func example() async throws"#)) XCTAssertMatch(testFileContents, .contains("func testExample() throws")) -#if canImport(Testing) +#if canImport(TestingDisabled) // Try building it await XCTAssertBuilds(path) let triple = try UserToolchain.default.targetTriple @@ -255,7 +255,7 @@ final class InitTests: XCTestCase { XCTAssertNoSuchPath(path.appending("Tests")) -#if canImport(Testing) +#if canImport(TestingDisabled) // Try building it await XCTAssertBuilds(path) let triple = try UserToolchain.default.targetTriple