From 69c163223ff0f3b38c4542670df5d9f6291d22b6 Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Fri, 19 Dec 2025 16:34:05 -0500 Subject: [PATCH 1/2] Tests: Migrate and Augment Resources and ModuleMaps Tests Migrate the ResourcesTests and ModuleMap Tests to Swift Testing and augment them to build the cross matrix of `debug` and `release` build configuration with `native` and `swiftbuild` build systems Relates to #8997 --- .../SwiftTesting+Tags.swift | 4 + Tests/BuildMetalTests/BuildMetalTests.swift | 34 +- Tests/FunctionalTests/ModuleMapTests.swift | 121 ++++-- Tests/FunctionalTests/ResourcesTests.swift | 356 +++++++++++------- 4 files changed, 333 insertions(+), 182 deletions(-) diff --git a/Sources/_InternalTestSupport/SwiftTesting+Tags.swift b/Sources/_InternalTestSupport/SwiftTesting+Tags.swift index 1801424cfbd..b4479b79a0c 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Tags.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Tags.swift @@ -35,6 +35,10 @@ extension Tag.FunctionalArea { @Tag public static var IndexMode: Tag @Tag public static var Sanitizer: Tag @Tag public static var LinkSwiftStaticStdlib: Tag + @Tag public static var Metal: Tag + @Tag public static var ModuleMaps: Tag + @Tag public static var Resources: Tag + } extension Tag.Feature { diff --git a/Tests/BuildMetalTests/BuildMetalTests.swift b/Tests/BuildMetalTests/BuildMetalTests.swift index d33481571f1..27c187195fc 100644 --- a/Tests/BuildMetalTests/BuildMetalTests.swift +++ b/Tests/BuildMetalTests/BuildMetalTests.swift @@ -14,24 +14,33 @@ import _InternalTestSupport import Testing import Basics import Foundation +import struct SPMBuildCore.BuildSystemProvider +import enum PackageModel.BuildConfiguration #if os(macOS) import Metal #endif -@Suite +@Suite( + .tags( + .FunctionalArea.Metal, + ) +) struct BuildMetalTests { -#if os(macOS) @Test( .disabled("Require downloadable Metal toolchain"), - .tags(.TestSize.large), + .tags( + .TestSize.large, + ), .requireHostOS(.macOS), - arguments: getBuildData(for: [.swiftbuild]) + arguments: BuildConfiguration.allCases, ) - func simpleLibrary(data: BuildData) async throws { - let buildSystem = data.buildSystem - let configuration = data.config - + func simpleLibrary( + config: BuildConfiguration, + ) async throws { + let buildSystem = BuildSystemProvider.Kind.swiftbuild + let configuration = config + try await fixture(name: "Metal/SimpleLibrary") { fixturePath in // Build the package @@ -41,7 +50,7 @@ struct BuildMetalTests { buildSystem: buildSystem, throwIfCommandFails: true ) - + // Get the bin path let (binPathOutput, _) = try await executeSwiftBuild( fixturePath, @@ -50,9 +59,9 @@ struct BuildMetalTests { buildSystem: buildSystem, throwIfCommandFails: true ) - + let binPath = try AbsolutePath(validating: binPathOutput.trimmingCharacters(in: .whitespacesAndNewlines)) - + // Check that default.metallib exists let metallibPath = binPath.appending(components:["MyRenderer_MyRenderer.bundle", "Contents", "Resources", "default.metallib"]) #expect( @@ -60,12 +69,13 @@ struct BuildMetalTests { "Expected default.metallib to exist at \(metallibPath)" ) +#if os(macOS) // Verify we can load the metal library let device = try #require(MTLCreateSystemDefaultDevice()) let library = try device.makeLibrary(URL: URL(fileURLWithPath: metallibPath.pathString)) #expect(library.functionNames.contains("simpleVertexShader")) +#endif } } -#endif } diff --git a/Tests/FunctionalTests/ModuleMapTests.swift b/Tests/FunctionalTests/ModuleMapTests.swift index 94d72a6ac3a..375ff6b716b 100644 --- a/Tests/FunctionalTests/ModuleMapTests.swift +++ b/Tests/FunctionalTests/ModuleMapTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2026 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -10,25 +10,37 @@ // //===----------------------------------------------------------------------===// +import Foundation + import Basics import Commands import PackageModel import _InternalTestSupport import Workspace -import XCTest +import Testing + +import struct SPMBuildCore.BuildSystemProvider -final class ModuleMapsTestCase: XCTestCase { - private func fixtureXCTest( +@Suite( + .serialized, // crash occurs when executed in parallel. needs investigation + .tags( + .FunctionalArea.ModuleMaps, + ), +) +struct ModuleMapsTestCase { + private func localFixture( name: String, cModuleName: String, rootpkg: String, + buildSystem: BuildSystemProvider.Kind, + config: BuildConfiguration, body: @escaping (AbsolutePath, [String]) async throws -> Void ) async throws { - try await _InternalTestSupport.fixtureXCTest(name: name) { fixturePath in + try await fixture(name: name) { fixturePath in let input = fixturePath.appending(components: cModuleName, "C", "foo.c") - let triple = try UserToolchain.default.targetTriple - let outdir = fixturePath.appending(components: rootpkg, ".build", triple.platformBuildPathComponent, "debug") + let outdir = try fixturePath.appending(components: [rootpkg] + buildSystem.binPath(for: config)) try makeDirectories(outdir) + let triple = try UserToolchain.default.targetTriple let output = outdir.appending("libfoo\(triple.dynamicLibraryExtension)") try await AsyncProcess.checkNonZeroExit(args: executableName("clang"), "-shared", input.pathString, "-o", output.pathString) @@ -41,43 +53,70 @@ final class ModuleMapsTestCase: XCTestCase { } } - func testDirectDependency() async throws { - try XCTSkipOnWindows(because: "fails to build on windows (maybe not supported?)") - try await fixtureXCTest(name: "ModuleMaps/Direct", cModuleName: "CFoo", rootpkg: "App") { fixturePath, Xld in - await XCTAssertBuilds( - fixturePath.appending("App"), - Xld: Xld, - buildSystem: .native, - ) + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func directDependency( + buildData: BuildData, + ) async throws { + let configuration = buildData.config + let buildSystem = buildData.buildSystem + try await withKnownIssue(isIntermittent: true) { + try await localFixture( + name: "ModuleMaps/Direct", + cModuleName: "CFoo", + rootpkg: "App", + buildSystem: buildSystem, + config: configuration, + ) { fixturePath, Xld in + try await executeSwiftBuild( + fixturePath.appending("App"), + configuration: configuration, + Xld: Xld, + buildSystem: buildSystem, + ) - let triple = try UserToolchain.default.targetTriple - let targetPath = fixturePath.appending(components: "App", ".build", triple.platformBuildPathComponent) - let debugout = try await AsyncProcess.checkNonZeroExit( - args: targetPath.appending(components: "debug", "App").pathString - ) - XCTAssertEqual(debugout, "123\n") - let releaseout = try await AsyncProcess.checkNonZeroExit( - args: targetPath.appending(components: "release", "App").pathString - ) - XCTAssertEqual(releaseout, "123\n") + let executable = try fixturePath.appending(components: ["App"] + buildSystem.binPath(for: configuration) + ["App"]) + let releaseout = try await AsyncProcess.checkNonZeroExit( + args: executable.pathString + ) + #expect(releaseout == "123\n") + } + } when: { + ProcessInfo.hostOperatingSystem == .windows + || (buildSystem == .swiftbuild && configuration == .release) } } - func testTransitiveDependency() async throws { - try XCTSkipOnWindows(because: "fails to build on windows (maybe not supported?)") - try await fixtureXCTest(name: "ModuleMaps/Transitive", cModuleName: "packageD", rootpkg: "packageA") { fixturePath, Xld in - await XCTAssertBuilds( - fixturePath.appending("packageA"), - Xld: Xld, - buildSystem: .native, - ) - - func verify(_ conf: String) async throws { - let triple = try UserToolchain.default.targetTriple + @Test( + .serialized, // crash occurs when executed in parallel. needs investigation + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func transitiveDependency( + buildData: BuildData, + ) async throws { + let configuration = buildData.config + let buildSystem = buildData.buildSystem + try await withKnownIssue(isIntermittent: true) { + try await localFixture( + name: "ModuleMaps/Transitive", + cModuleName: "packageD", + rootpkg: "packageA", + buildSystem: buildSystem, + config: configuration, + ) { fixturePath, Xld in + try await executeSwiftBuild( + fixturePath.appending("packageA"), + configuration: configuration, + Xld: Xld, + buildSystem: buildSystem, + ) + + let executable = try fixturePath.appending(components: ["packageA"] + buildSystem.binPath(for: configuration) + ["packageA"]) let out = try await AsyncProcess.checkNonZeroExit( - args: fixturePath.appending(components: "packageA", ".build", triple.platformBuildPathComponent, conf, "packageA").pathString + args: executable.pathString ) - XCTAssertEqual(out, """ + #expect(out == """ calling Y.bar() Y.bar() called X.foo() called @@ -85,9 +124,9 @@ final class ModuleMapsTestCase: XCTestCase { """) } - - try await verify("debug") - try await verify("release") + } when: { + ProcessInfo.hostOperatingSystem == .windows + || (buildSystem == .swiftbuild && configuration == .release) } } } diff --git a/Tests/FunctionalTests/ResourcesTests.swift b/Tests/FunctionalTests/ResourcesTests.swift index 0d5eeae6a22..a0a4e9bbe71 100644 --- a/Tests/FunctionalTests/ResourcesTests.swift +++ b/Tests/FunctionalTests/ResourcesTests.swift @@ -2,59 +2,86 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation import Basics import PackageModel import _InternalTestSupport -import XCTest - -final class ResourcesTests: XCTestCase { - func testSimpleResources() async throws { - try XCTSkipOnWindows( - because: """ - Invalid path. Possibly related to https://github.com/swiftlang/swift-package-manager/issues/8511 or https://github.com/swiftlang/swift-package-manager/issues/8602 - """, - skipPlatformCi: true, - ) - - try await fixtureXCTest(name: "Resources/Simple") { fixturePath in - var executables = ["SwiftyResource"] +import Testing - // Objective-C module requires macOS - #if os(macOS) - executables.append("SeaResource") - executables.append("CPPResource") - #endif +@Suite( + .tags( + .TestSize.large, + .FunctionalArea.Resources, + ), + ) - for execName in executables { - let (output, _) = try await executeSwiftRun( - fixturePath, - execName, - buildSystem: .native, - ) - XCTAssertTrue(output.contains("foo"), output) +struct ResourcesTests{ + @Test( + .IssueWindowsRelativePathAssert, + .IssueWindowsPathTestsFailures, + .tags( + .Feature.Command.Run, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func simpleResources( + buildData: BuildData, + ) async throws { + try await withKnownIssue(isIntermittent: true) { + try await fixture(name: "Resources/Simple") { fixturePath in + var executables = ["SwiftyResource"] + + // Objective-C module requires macOS + #if os(macOS) + executables.append("SeaResource") + executables.append("CPPResource") + #endif + + for execName in executables { + let (output, _) = try await executeSwiftRun( + fixturePath, + execName, + configuration: buildData.config, + buildSystem: buildData.buildSystem, + ) + #expect(output.contains("foo")) + } } + } when: { + ProcessInfo.hostOperatingSystem == .windows } } - func testLocalizedResources() async throws { - try await fixtureXCTest(name: "Resources/Localized") { fixturePath in + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func localizedResources( + buildData: BuildData + ) async throws { + let configuration = buildData.config + let buildSystem = buildData.buildSystem + try await fixture(name: "Resources/Localized") { fixturePath in try await executeSwiftBuild( fixturePath, - buildSystem: .native, + configuration: configuration, + buildSystem: buildSystem, ) - let exec = AbsolutePath(".build/debug/exe", relativeTo: fixturePath) + let exec = try fixturePath.appending(components: buildSystem.binPath(for: configuration) + [executableName("exe")]) // Note: Source from LANG and -AppleLanguages on command line for Linux resources let output = try await AsyncProcess.checkNonZeroExit(args: exec.pathString, "-AppleLanguages", "(en_US)").withSwiftLineEnding - XCTAssertEqual(output, """ + #expect(output == """ ¡Hola Mundo! Hallo Welt! Bonjour le monde ! @@ -63,23 +90,43 @@ final class ResourcesTests: XCTestCase { } } - func testResourcesInMixedClangPackage() async throws { - #if !os(macOS) - // Running swift-test fixtures on linux is not yet possible. - try XCTSkipIf(true, "test is only supported on macOS") - #endif - - try await fixtureXCTest(name: "Resources/Simple") { fixturePath in - await XCTAssertBuilds( + @Test( + // .requireHostOS(.macOS), // Test was originally only enabled on macOS + .skipHostOS(.linux), // currently failing on Ubuntu + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func resourcesInMixedClangPackage( + buildData: BuildData, + ) async throws { + // #if !os(macOS) + // // Running swift-test fixtures on linux is not yet possible. + // try XCTSkipIf(true, "test is only supported on macOS") + // #endif + try await fixture(name: "Resources/Simple") { fixturePath in + try await executeSwiftBuild( fixturePath, + configuration: buildData.config, extraArgs: ["--target", "MixedClangResource"], - buildSystem: .native, + buildSystem: buildData.buildSystem, ) } } - func testMovedBinaryResources() async throws { - try await fixtureXCTest(name: "Resources/Moved") { fixturePath in + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func movedBinaryResources( + buildData: BuildData, + ) async throws { + let configuration = buildData.config + let buildSystem = buildData.buildSystem + try await fixture(name: "Resources/Moved") { fixturePath in var executables = ["SwiftyResource"] // Objective-C module requires macOS @@ -90,19 +137,18 @@ final class ResourcesTests: XCTestCase { let binPath = try AbsolutePath(validating: await executeSwiftBuild( fixturePath, - configuration: .release, + configuration: configuration, extraArgs: ["--show-bin-path"], - buildSystem: .native, - ).stdout - .trimmingCharacters(in: .whitespacesAndNewlines) + buildSystem: buildSystem, + ).stdout.trimmingCharacters(in: .whitespacesAndNewlines) ) for execName in executables { _ = try await executeSwiftBuild( fixturePath, - configuration: .release, + configuration: configuration, extraArgs: ["--product", execName], - buildSystem: .native, + buildSystem: buildSystem, ) try await withTemporaryDirectory(prefix: execName) { tmpDirPath in @@ -122,114 +168,166 @@ final class ResourcesTests: XCTestCase { .forEach { try localFileSystem.move(from: binPath.appending(component: $0), to: tmpDirPath.appending(component: $0)) } // Run the binary let output = try await AsyncProcess.checkNonZeroExit(args: destBinPath.pathString) - XCTAssertMatch(output, .contains("foo")) + #expect(output.contains("foo")) } } } } - func testSwiftResourceAccessorDoesNotCauseInconsistentImportWarning() async throws { - try XCTSkipOnWindows(because: "fails to build, need investigation") - try await fixtureXCTest(name: "Resources/FoundationlessClient/UtilsWithFoundationPkg") { fixturePath in - await XCTAssertBuilds( + @Test( + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func swiftResourceAccessorDoesNotCauseInconsistentImportWarning( + buildData: BuildData, + ) async throws { + // try XCTSkipOnWindows(because: "fails to build, need investigation") + try await fixture(name: "Resources/FoundationlessClient/UtilsWithFoundationPkg") { fixturePath in + try await executeSwiftBuild( fixturePath, + configuration: buildData.config, Xswiftc: ["-warnings-as-errors"], - buildSystem: .native, + buildSystem: buildData.buildSystem, ) } } - func testResourceBundleInClangPackageWhenRunningSwiftTest() async throws { - #if !os(macOS) - // Running swift-test fixtures on linux is not yet possible. - try XCTSkipIf(true, "test is only supported on macOS") - #endif + @Test( + .tags( + .Feature.Command.Test, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func resourceBundleInClangPackageWhenRunningSwiftTest( + buildData: BuildData, + ) async throws { + // #if !os(macOS) + // // Running swift-test fixtures on linux is not yet possible. + // try XCTSkipIf(true, "test is only supported on macOS") + // #endif - try await fixtureXCTest(name: "Resources/Simple") { fixturePath in - await XCTAssertSwiftTest( + try await fixture(name: "Resources/Simple") { fixturePath in + try await executeSwiftTest( fixturePath, + configuration: buildData.config, extraArgs: ["--filter", "ClangResourceTests"], - buildSystem: .native, + buildSystem: buildData.buildSystem, ) } } - func testResourcesEmbeddedInCode() async throws { - try await fixtureXCTest(name: "Resources/EmbedInCodeSimple") { fixturePath in - let execPath = fixturePath.appending(components: ".build", "debug", "EmbedInCodeSimple") - try await executeSwiftBuild( - fixturePath, - buildSystem: .native, - ) - let result = try await AsyncProcess.checkNonZeroExit(args: execPath.pathString) - XCTAssertMatch(result, .contains("hello world")) - let resourcePath = fixturePath.appending( - components: "Sources", "EmbedInCodeSimple", "best.txt") - - // Check incremental builds - for i in 0..<2 { - let content = "Hi there \(i)!" - // Update the resource file. - try localFileSystem.writeFileContents(resourcePath, string: content) + @Test( + .serialized, // crash occurs when executed in parallel. needs investigation + .issue("https://github.com/swiftlang/swift-package-manager/issues/9528", relationship: .defect), + .tags( + .Feature.Command.Build, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func resourcesEmbeddedInCode( + buildData: BuildData, + ) async throws { + let configuration = buildData.config + let buildSystem = buildData.buildSystem + try await withKnownIssue { + try await fixture(name: "Resources/EmbedInCodeSimple") { fixturePath in + let execPath = try fixturePath.appending(components: buildSystem.binPath(for: configuration) + [executableName("EmbedInCodeSimple")]) try await executeSwiftBuild( fixturePath, - buildSystem: .native, + configuration: configuration, + buildSystem: buildSystem, ) - // Run the executable again. - let result2 = try await AsyncProcess.checkNonZeroExit(args: execPath.pathString) - XCTAssertMatch(result2, .contains("\(content)")) + let result = try await AsyncProcess.checkNonZeroExit(args: execPath.pathString) + #expect(result.contains("hello world")) + let resourcePath = fixturePath.appending( + components: "Sources", "EmbedInCodeSimple", "best.txt") + + // Check incremental builds + for i in 0..<2 { + let content = "Hi there \(i)!" + // Update the resource file. + try localFileSystem.writeFileContents(resourcePath, string: content) + try await executeSwiftBuild( + fixturePath, + configuration: configuration, + buildSystem: buildSystem, + ) + // Run the executable again. + let result2 = try await AsyncProcess.checkNonZeroExit(args: execPath.pathString) + #expect(result2.contains("\(content)")) + } } + } when: { + buildSystem == .swiftbuild } } - func testResourcesOutsideOfTargetCanBeIncluded() async throws { - try await testWithTemporaryDirectory { tmpPath in - let packageDir = tmpPath.appending(components: "MyPackage") - - let manifestFile = packageDir.appending("Package.swift") - try localFileSystem.createDirectory(manifestFile.parentDirectory, recursive: true) - try localFileSystem.writeFileContents( - manifestFile, - string: """ - // swift-tools-version: 6.0 - import PackageDescription - let package = Package(name: "MyPackage", - targets: [ - .executableTarget( - name: "exec", - resources: [.copy("../resources")] - ) - ]) + @Test( + .serialized, // crash occurs when executed in parallel. needs investigation + .tags( + .Feature.Command.Test, + ), + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func resourcesOutsideOfTargetCanBeIncluded( + buildData: BuildData, + ) async throws { + let configuration = buildData.config + let buildSystem = buildData.buildSystem + try await withKnownIssue { + try await testWithTemporaryDirectory { tmpPath in + let packageDir = tmpPath.appending(components: "MyPackage") + + let manifestFile = packageDir.appending("Package.swift") + try localFileSystem.createDirectory(manifestFile.parentDirectory, recursive: true) + try localFileSystem.writeFileContents( + manifestFile, + string: """ + // swift-tools-version: 6.0 + import PackageDescription + let package = Package(name: "MyPackage", + targets: [ + .executableTarget( + name: "exec", + resources: [.copy("../resources")] + ) + ]) + """) + + let targetSourceFile = packageDir.appending(components: "Sources", "exec", "main.swift") + try localFileSystem.createDirectory(targetSourceFile.parentDirectory, recursive: true) + try localFileSystem.writeFileContents(targetSourceFile, string: """ + import Foundation + print(Bundle.module.resourcePath ?? "") """) - let targetSourceFile = packageDir.appending(components: "Sources", "exec", "main.swift") - try localFileSystem.createDirectory(targetSourceFile.parentDirectory, recursive: true) - try localFileSystem.writeFileContents(targetSourceFile, string: """ - import Foundation - print(Bundle.module.resourcePath ?? "") - """) - - let resource = packageDir.appending(components: "Sources", "resources", "best.txt") - try localFileSystem.createDirectory(resource.parentDirectory, recursive: true) - try localFileSystem.writeFileContents(resource, string: "best") - - let (_, stderr) = try await executeSwiftBuild( - packageDir, - env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"], - buildSystem: .native, - ) - // Filter some unrelated output that could show up on stderr. - let filteredStderr = stderr.components(separatedBy: "\n").filter { !$0.contains("[logging]") } - .filter { !$0.contains("Unable to locate libSwiftScan") }.joined(separator: "\n") - XCTAssertEqual(filteredStderr, "", "unexpectedly received error output: \(stderr)") - - let builtProductsDir = packageDir.appending(components: [".build", "debug"]) - // On Apple platforms, it's going to be `.bundle` and elsewhere `.resources`. - let potentialResourceBundleName = try XCTUnwrap(localFileSystem.getDirectoryContents(builtProductsDir).filter { $0.hasPrefix("MyPackage_exec.") }.first) - let resourcePath = builtProductsDir.appending(components: [potentialResourceBundleName, "resources", "best.txt"]) - XCTAssertTrue(localFileSystem.exists(resourcePath), "resource file wasn't copied by the build") - let contents = try String(contentsOfFile: resourcePath.pathString) - XCTAssertEqual(contents, "best", "unexpected resource contents: \(contents)") + let resource = packageDir.appending(components: "Sources", "resources", "best.txt") + try localFileSystem.createDirectory(resource.parentDirectory, recursive: true) + try localFileSystem.writeFileContents(resource, string: "best") + + let (_, stderr) = try await executeSwiftBuild( + packageDir, + configuration: configuration, + env: ["SWIFT_DRIVER_SWIFTSCAN_LIB" : "/this/is/a/bad/path"], + buildSystem: buildSystem, + ) + // Filter some unrelated output that could show up on stderr. + let filteredStderr = stderr.components(separatedBy: "\n").filter { !$0.contains("[logging]") } + .filter { !$0.contains("Unable to locate libSwiftScan") }.joined(separator: "\n") + #expect(filteredStderr == "", "unexpectedly received error output: \(stderr)") + + let builtProductsDir = try packageDir.appending(components: buildSystem.binPath(for: configuration)) + // On Apple platforms, it's going to be `.bundle` and elsewhere `.resources`. + let potentialResourceBundleName = try #require(localFileSystem.getDirectoryContents(builtProductsDir).filter { $0.hasPrefix("MyPackage_exec.") }.first) + let resourcePath = builtProductsDir.appending(components: [potentialResourceBundleName, "resources", "best.txt"]) + #expect(localFileSystem.exists(resourcePath), "resource file wasn't copied by the build") + let contents = try String(contentsOfFile: resourcePath.pathString) + #expect(contents == "best", "unexpected resource contents: \(contents)") + } + } when: { + buildSystem == .swiftbuild } } } From 3f6170cc4a48032d60563f8e93fee8d91843bed5 Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Fri, 19 Dec 2025 22:07:21 -0500 Subject: [PATCH 2/2] Tests: Migrate InitTests to Swift testing and Augment Migrate the InitTests to Swift Testing and, where applicable, augment the tests to run against the matrix of `debug` and `release` build configuration with the `native` and `swiftbuild` build systems. Relates to #8997 --- .../SwiftTesting+Helpers.swift | 31 +- .../SwiftTesting+Tags.swift | 2 +- Tests/WorkspaceTests/InitTests.swift | 827 +++++++++++------- 3 files changed, 516 insertions(+), 344 deletions(-) diff --git a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift index 9131e584dab..1bb901519b0 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Helpers.swift @@ -13,11 +13,10 @@ import Basics import Testing -public func expectFileExists( - at path: AbsolutePath, - _ comment: Comment? = nil, - sourceLocation: SourceLocation = #_sourceLocation, -) { +private func fileExistsErrorMessage( + for path: AbsolutePath, + comment: Comment? +) -> Comment { let commentPrefix = if let comment { "\(comment): " @@ -30,9 +29,29 @@ public func expectFileExists( } catch { msgSuffix = "" } + return Comment("\(commentPrefix)File '\(path)' does not exist. \(msgSuffix)") +} + +public func expectFileExists( + at path: AbsolutePath, + _ comment: Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, +) { #expect( localFileSystem.exists(path), - "\(commentPrefix)File '\(path)' does not exist. \(msgSuffix)", + fileExistsErrorMessage(for: path, comment: comment), + sourceLocation: sourceLocation, + ) +} + +public func requireFileExists( + at path: AbsolutePath, + _ comment: Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, +) throws { + try #require( + localFileSystem.exists(path), + fileExistsErrorMessage(for: path, comment: comment), sourceLocation: sourceLocation, ) } diff --git a/Sources/_InternalTestSupport/SwiftTesting+Tags.swift b/Sources/_InternalTestSupport/SwiftTesting+Tags.swift index b4479b79a0c..bd52f57e8c8 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Tags.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Tags.swift @@ -38,7 +38,7 @@ extension Tag.FunctionalArea { @Tag public static var Metal: Tag @Tag public static var ModuleMaps: Tag @Tag public static var Resources: Tag - + @Tag public static var Workspace: Tag } extension Tag.Feature { diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index ddc8efe0b92..c1b24080523 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors +// Copyright (c) 2014-2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -14,19 +14,31 @@ import Basics import _InternalTestSupport import PackageModel import Workspace -import XCTest +import Testing -final class InitTests: XCTestCase { +import struct SPMBuildCore.BuildSystemProvider + +@Suite( + .tags( + .FunctionalArea.Workspace, + ), +) +struct InitTests { // MARK: TSCBasic package creation for each package type. - - func testInitPackageEmpty() throws { - try testWithTemporaryDirectory { tmpPath in + + @Test( + .tags( + .TestSize.medium, + ), + ) + func initPackageEmpty() throws { + try withTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") let name = path.basename try fs.createDirectory(path) - + // Create the package let initPackage = try InitPackage( name: name, @@ -41,264 +53,431 @@ final class InitTests: XCTestCase { try initPackage.writePackageStructure() // Not picky about the specific progress messages, just checking that we got some. - XCTAssertGreaterThan(progressMessages.count, 0) + #expect(progressMessages.count > 0) // Verify basic file system content that we expect in the package let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) + try requireFileExists(at: manifest) + let manifestContents: String = try localFileSystem.readFileContents(manifest) let version = InitPackage.newPackageToolsVersion let versionSpecifier = "\(version.major).\(version.minor)" - XCTAssertMatch(manifestContents, .prefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) - XCTAssertMatch(manifestContents, .contains(packageWithNameOnly(named: name))) + #expect(manifestContents.hasPrefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) + #expect(manifestContents.contains(packageWithNameOnly(named: name))) } } - func testInitPackageExecutable() async throws { - try await testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("Foo") - let name = path.basename - try fs.createDirectory(path) - - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .executable, - destinationPath: path, - fileSystem: localFileSystem - ) - var progressMessages = [String]() - initPackage.progressReporter = { message in - progressMessages.append(message) + @Suite( + .serialized, // Crash occurred when executed in parallel. Needs investigation + .tags( + .TestSize.large, + ), + ) + struct InitTestsThatPerformABuild { + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func initPackageExecutable( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try await testWithTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("Foo") + let name = path.basename + try fs.createDirectory(path) + + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .executable, + destinationPath: path, + fileSystem: localFileSystem + ) + var progressMessages = [String]() + initPackage.progressReporter = { message in + progressMessages.append(message) + } + try initPackage.writePackageStructure() + + // Not picky about the specific progress messages, just checking that we got some. + #expect(progressMessages.count > 0) + + // Verify basic file system content that we expect in the package + let manifest = path.appending("Package.swift") + try requireFileExists(at: manifest) + + let manifestContents: String = try localFileSystem.readFileContents(manifest) + let version = InitPackage.newPackageToolsVersion + let versionSpecifier = "\(version.major).\(version.minor)" + #expect(manifestContents.hasPrefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) + + #expect(try fs.getDirectoryContents(path.appending("Sources").appending("Foo")) == ["Foo.swift"]) + try await executeSwiftBuild( + path, + configuration: configuration, + buildSystem: buildSystem, + ) + let binPath = try path.appending(components: buildSystem.binPath(for: configuration)) + expectFileExists(at: binPath.appending(executableName("Foo"))) + let expectedOutput: [String] + switch buildSystem { + case .native: + expectedOutput = ["Modules", "Foo.swiftmodule"] + case .swiftbuild: + expectedOutput = ["Foo.swiftmodule"] + case .xcode: + expectedOutput = ["Foo.swiftmodule"] + Issue.record("Test expectation is not implemented") + } + let expectedFile = binPath.appending(components: expectedOutput) + + expectFileExists(at: expectedFile) } - try initPackage.writePackageStructure() - - // Not picky about the specific progress messages, just checking that we got some. - XCTAssertGreaterThan(progressMessages.count, 0) - - // 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) - let version = InitPackage.newPackageToolsVersion - let versionSpecifier = "\(version.major).\(version.minor)" - XCTAssertMatch(manifestContents, .prefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) - - XCTAssertEqual(try fs.getDirectoryContents(path.appending("Sources").appending("Foo")), ["Foo.swift"]) - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - let binPath = path.appending(components: ".build", triple.platformBuildPathComponent, "debug") -#if os(Windows) - XCTAssertFileExists(binPath.appending("Foo.exe")) -#else - XCTAssertFileExists(binPath.appending("Foo")) -#endif - XCTAssertFileExists(binPath.appending(components: "Modules", "Foo.swiftmodule")) } - } - func testInitPackageExecutableCalledMain() async throws { - try await testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("main") - let name = path.basename - try fs.createDirectory(path) - - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .executable, - destinationPath: path, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() - - XCTAssertEqual(try fs.getDirectoryContents(path.appending("Sources").appending("main")), ["MainEntrypoint.swift"]) - await XCTAssertBuilds( - path, - buildSystem: .native, - ) + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func initPackageExecutableCalledMain( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try await testWithTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("main") + let name = path.basename + try fs.createDirectory(path) + + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .executable, + destinationPath: path, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + + let contents = try fs.getDirectoryContents(path.appending("Sources").appending("main")) + try #require(contents == ["MainEntrypoint.swift"]) + try await executeSwiftBuild( + path, + configuration: configuration, + buildSystem: buildSystem, + ) + } } - } - - func testInitPackageLibraryWithXCTestOnly() async throws { - try await testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("Foo") - let name = path.basename - try fs.createDirectory(path) - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .library, - supportedTestingLibraries: [.xctest], - destinationPath: path, - fileSystem: localFileSystem - ) - var progressMessages = [String]() - initPackage.progressReporter = { message in - progressMessages.append(message) + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func initPackageLibraryWithXCTestOnly( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try await withTemporaryDirectory(removeTreeOnDeinit: true) { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("Foo") + let name = path.basename + try fs.createDirectory(path) + + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .library, + supportedTestingLibraries: [.xctest], + destinationPath: path, + fileSystem: localFileSystem + ) + var progressMessages = [String]() + initPackage.progressReporter = { message in + progressMessages.append(message) + } + try initPackage.writePackageStructure() + + // Not picky about the specific progress messages, just checking that we got some. + #expect(progressMessages.count > 0) + + // Verify basic file system content that we expect in the package + let manifest = path.appending("Package.swift") + try requireFileExists(at: manifest) + let manifestContents: String = try localFileSystem.readFileContents(manifest) + let version = InitPackage.newPackageToolsVersion + let versionSpecifier = "\(version.major).\(version.minor)" + #expect(manifestContents.hasPrefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) + + #expect(try fs.getDirectoryContents(path.appending("Sources").appending("Foo")) == ["Foo.swift"]) + + let tests = path.appending("Tests") + #expect(try fs.getDirectoryContents(tests).sorted() == ["FooTests"]) + + let testFile = tests.appending("FooTests").appending("FooTests.swift") + let testFileContents: String = try localFileSystem.readFileContents(testFile) + #expect(testFileContents.hasPrefix("import XCTest"), """ + Validates formatting of XCTest source file, in particular that it does not contain leading whitespace: + \(testFileContents) + """) + #expect(testFileContents.contains("func testExample() throws")) + + // Try building it + try await executeSwiftBuild( + path, + configuration: configuration, + buildSystem: buildSystem, + ) + let expectedOutput: [String] + switch buildSystem { + case .native: + expectedOutput = ["Modules", "Foo.swiftmodule"] + case .swiftbuild: + expectedOutput = ["Foo.swiftmodule"] + case .xcode: + expectedOutput = ["Foo.swiftmodule"] + Issue.record("Test expectation is not implemented") + } + let expectedFile = try path.appending(components: buildSystem.binPath(for: configuration) + expectedOutput) + expectFileExists(at: expectedFile) } - try initPackage.writePackageStructure() - - // Not picky about the specific progress messages, just checking that we got some. - XCTAssertGreaterThan(progressMessages.count, 0) - - // 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) - let version = InitPackage.newPackageToolsVersion - let versionSpecifier = "\(version.major).\(version.minor)" - XCTAssertMatch(manifestContents, .prefix("// swift-tools-version:\(version < .v5_4 ? "" : " ")\(versionSpecifier)\n")) - - XCTAssertEqual(try fs.getDirectoryContents(path.appending("Sources").appending("Foo")), ["Foo.swift"]) - - let tests = path.appending("Tests") - XCTAssertEqual(try fs.getDirectoryContents(tests).sorted(), ["FooTests"]) - - let testFile = tests.appending("FooTests").appending("FooTests.swift") - let testFileContents: String = try localFileSystem.readFileContents(testFile) - XCTAssertTrue(testFileContents.hasPrefix("import XCTest"), """ - Validates formatting of XCTest source file, in particular that it does not contain leading whitespace: - \(testFileContents) - """) - XCTAssertMatch(testFileContents, .contains("func testExample() throws")) - - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) } - } - - func testInitPackageLibraryWithSwiftTestingOnly() async throws { - try testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("Foo") - let name = path.basename - try fs.createDirectory(path) - - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .library, - supportedTestingLibraries: [.swiftTesting], - destinationPath: path, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() - - // Verify basic file system content that we expect in the package - let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - 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() async throws"#)) - XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) - -#if canImport(TestingDisabled) - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) -#endif + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func initPackageLibraryWithSwiftTestingOnly( + buildData: BuildData, + ) async throws { + + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try withTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("Foo") + let name = path.basename + try fs.createDirectory(path) + + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .library, + supportedTestingLibraries: [.swiftTesting], + destinationPath: path, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + + // Verify basic file system content that we expect in the package + let manifest = path.appending("Package.swift") + try requireFileExists(at: manifest) + + let testFile = path.appending("Tests").appending("FooTests").appending("FooTests.swift") + let testFileContents: String = try localFileSystem.readFileContents(testFile) + #expect(testFileContents.contains(#"import Testing"#)) + #expect(!testFileContents.contains(#"import XCTest"#)) + #expect(testFileContents.contains(#"@Test func example() async throws"#)) + #expect(!testFileContents.contains("func testExample() throws")) + + #if canImport(TestingDisabled) + // Try building it + try await executeSwiftBuild( + path, + configuration: configuration, + buildSystem: buildSystem, + ) + let triple = try UserToolchain.default.targetTriple + expectFileExists(at: path.appending(components: buildSystem.binPath(for: configuration) + ["Modules", "Foo.swiftmodule"])) + #endif + } } - } - - func testInitPackageLibraryWithBothSwiftTestingAndXCTest() async throws { - try testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("Foo") - let name = path.basename - try fs.createDirectory(path) - - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .library, - supportedTestingLibraries: [.swiftTesting, .xctest], - destinationPath: path, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() - - // Verify basic file system content that we expect in the package - let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) - 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() async throws"#)) - XCTAssertMatch(testFileContents, .contains("func testExample() throws")) - -#if canImport(TestingDisabled) - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) -#endif + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func initPackageLibraryWithBothSwiftTestingAndXCTest( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try withTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("Foo") + let name = path.basename + try fs.createDirectory(path) + + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .library, + supportedTestingLibraries: [.swiftTesting, .xctest], + destinationPath: path, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + + // Verify basic file system content that we expect in the package + let manifest = path.appending("Package.swift") + try requireFileExists(at: manifest) + + let testFile = path.appending("Tests").appending("FooTests").appending("FooTests.swift") + let testFileContents: String = try localFileSystem.readFileContents(testFile) + #expect(testFileContents.contains(#"import Testing"#)) + #expect(testFileContents.contains(#"import XCTest"#)) + #expect(testFileContents.contains(#"@Test func example() async throws"#)) + #expect(testFileContents.contains("func testExample() throws")) + + #if canImport(TestingDisabled) + // Try building it + try await executeSwiftBuild( + path, + configuration: configuration, + buildSystem: buildSystem, + ) + let triple = try UserToolchain.default.targetTriple + expectFileExists(at: path.appending(components: buildSystem.binPath(for: configuration) + ["Modules", "Foo.swiftmodule"])) + #endif + } } - } - func testInitPackageLibraryWithNoTests() async throws { - try testWithTemporaryDirectory { tmpPath in - let fs = localFileSystem - let path = tmpPath.appending("Foo") - let name = path.basename - try fs.createDirectory(path) - - // Create the package - let initPackage = try InitPackage( - name: name, - packageType: .library, - supportedTestingLibraries: [], - destinationPath: path, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func initPackageLibraryWithNoTests( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try withTemporaryDirectory { tmpPath in + let fs = localFileSystem + let path = tmpPath.appending("Foo") + let name = path.basename + try fs.createDirectory(path) + + // Create the package + let initPackage = try InitPackage( + name: name, + packageType: .library, + supportedTestingLibraries: [], + destinationPath: path, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + + // Verify basic file system content that we expect in the package + let manifest = path.appending("Package.swift") + try requireFileExists(at: manifest) + + let manifestContents: String = try localFileSystem.readFileContents(manifest) + #expect(!manifestContents.contains(#".testTarget"#)) + + expectFileDoesNotExists(at: path.appending("Tests")) + + #if canImport(TestingDisabled) + // Try building it + try await executeSwiftBuild( + path, + configuration: configuration, + buildSystem: buildSystem, + ) + let triple = try UserToolchain.default.targetTriple + expectFileExists(at: path.appending(components: ".build", triple.platformBuildPathComponent, configuration.dirname, "Modules", "Foo.swiftmodule")) + #endif + } + } - // 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) - XCTAssertNoMatch(manifestContents, .contains(#".testTarget"#)) + // MARK: Special case testing - XCTAssertNoSuchPath(path.appending("Tests")) + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func initPackageNonc99Directory( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in + expectDirectoryExists(at: tempDirPath) + + // Create a directory with non c99name. + let packageRoot = tempDirPath.appending("some-package") + let packageName = packageRoot.basename + try localFileSystem.createDirectory(packageRoot) + expectDirectoryExists(at: packageRoot) + + // Create the package + let initPackage = try InitPackage( + name: packageName, + packageType: .library, + destinationPath: packageRoot, + fileSystem: localFileSystem + ) + initPackage.progressReporter = { message in } + try initPackage.writePackageStructure() + + // Try building it. + try await executeSwiftBuild( + packageRoot, + configuration: configuration, + buildSystem: buildSystem, + ) + + let expectedFile: AbsolutePath + switch buildSystem { + case .native: + expectedFile = try packageRoot.appending(components: buildSystem.binPath(for: configuration) + ["Modules", "some_package.swiftmodule"]) + case .swiftbuild: + expectedFile = try packageRoot.appending(components: buildSystem.binPath(for: configuration) + [ "some_package.swiftmodule"]) + case .xcode: + expectedFile = try packageRoot.appending(components: buildSystem.binPath(for: configuration) + [ "some_package.swiftmodule"]) + Issue.record("Test expectation is not implemented") + } + + expectFileExists(at: expectedFile) + } + } -#if canImport(TestingDisabled) - // Try building it - await XCTAssertBuilds( - path, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(path.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "Foo.swiftmodule")) -#endif + @Test( + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), + ) + func nonC99NameExecutablePackage( + buildData: BuildData, + ) async throws { + let buildSystem = buildData.buildSystem + let configuration = buildData.config + try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in + expectDirectoryExists(at: tempDirPath) + + let packageRoot = tempDirPath.appending("Foo") + try localFileSystem.createDirectory(packageRoot) + expectDirectoryExists(at: packageRoot) + + // Create package with non c99name. + let initPackage = try InitPackage( + name: "package-name", + packageType: .executable, + destinationPath: packageRoot, + fileSystem: localFileSystem + ) + try initPackage.writePackageStructure() + + try await executeSwiftBuild( + packageRoot, + configuration: configuration, + buildSystem: buildSystem, + ) + } } + } - func testInitPackageExecutableWithSwiftTesting() async throws { - try testWithTemporaryDirectory { tmpPath in + @Test( + .tags( + .TestSize.medium, + ), + ) + func initPackageExecutableWithSwiftTesting() async throws { + try withTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") let name = path.basename @@ -315,20 +494,26 @@ final class InitTests: XCTestCase { try initPackage.writePackageStructure() // Verify basic file system content that we expect in the package let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) + try requireFileExists(at: manifest) + let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .contains(".testTarget(")) + #expect(manifestContents.contains(".testTarget(")) 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() async throws"#)) - XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) + #expect(testFileContents.contains(#"import Testing"#)) + #expect(!testFileContents.contains(#"import XCTest"#)) + #expect(testFileContents.contains(#"@Test func example() async throws"#)) + #expect(!testFileContents.contains("func testExample() throws")) } } - func testInitPackageToolWithSwiftTesting() async throws { - try testWithTemporaryDirectory { tmpPath in + @Test( + .tags( + .TestSize.medium, + ), + ) + func initPackageToolWithSwiftTesting() async throws { + try withTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("Foo") let name = path.basename @@ -345,20 +530,26 @@ final class InitTests: XCTestCase { try initPackage.writePackageStructure() // Verify basic file system content that we expect in the package let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) + try requireFileExists(at: manifest) + let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .contains(".testTarget(")) + #expect(manifestContents.contains(".testTarget(")) 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() async throws"#)) - XCTAssertNoMatch(testFileContents, .contains("func testExample() throws")) + #expect(testFileContents.contains(#"import Testing"#)) + #expect(!testFileContents.contains(#"import XCTest"#)) + #expect(testFileContents.contains(#"@Test func example() async throws"#)) + #expect(!testFileContents.contains("func testExample() throws")) } } - func testInitPackageCommandPlugin() throws { - try testWithTemporaryDirectory { tmpPath in + @Test( + .tags( + .TestSize.medium, + ), + ) + func initPackageCommandPlugin() throws { + try withTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("MyCommandPlugin") let name = path.basename @@ -374,26 +565,33 @@ final class InitTests: XCTestCase { // Verify basic file system content that we expect in the package let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) + try requireFileExists(at: manifest) + let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), .contains("targets: [\"MyCommandPlugin\"]"))) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), - .and(.contains("capability: .command(intent: .custom("), .contains("verb: \"MyCommandPlugin\"")))) + #expect(manifestContents.contains(".plugin(") && manifestContents.contains("targets: [\"MyCommandPlugin\"]")) + #expect(manifestContents.contains(".plugin(") && + manifestContents.contains("capability: .command(intent: .custom(") && + manifestContents.contains("verb: \"MyCommandPlugin\"")) // Check basic content that we expect in the plugin source file let source = path.appending("Plugins", "MyCommandPlugin.swift") - XCTAssertFileExists(source) + expectFileExists(at: source) let sourceContents: String = try localFileSystem.readFileContents(source) - XCTAssertMatch(sourceContents, .contains("struct MyCommandPlugin: CommandPlugin")) - XCTAssertMatch(sourceContents, .contains("performCommand(context: PluginContext")) - XCTAssertMatch(sourceContents, .contains("import XcodeProjectPlugin")) - XCTAssertMatch(sourceContents, .contains("extension MyCommandPlugin: XcodeCommandPlugin")) - XCTAssertMatch(sourceContents, .contains("performCommand(context: XcodePluginContext")) + #expect(sourceContents.contains("struct MyCommandPlugin: CommandPlugin")) + #expect(sourceContents.contains("performCommand(context: PluginContext")) + #expect(sourceContents.contains("import XcodeProjectPlugin")) + #expect(sourceContents.contains("extension MyCommandPlugin: XcodeCommandPlugin")) + #expect(sourceContents.contains("performCommand(context: XcodePluginContext")) } } - - func testInitPackageBuildToolPlugin() throws { - try testWithTemporaryDirectory { tmpPath in + + @Test( + .tags( + .TestSize.medium, + ), + ) + func initPackageBuildToolPlugin() throws { + try withTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("MyBuildToolPlugin") let name = path.basename @@ -409,80 +607,30 @@ final class InitTests: XCTestCase { // Verify basic file system content that we expect in the package let manifest = path.appending("Package.swift") - XCTAssertFileExists(manifest) + try requireFileExists(at: manifest) + let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), .contains("targets: [\"MyBuildToolPlugin\"]"))) - XCTAssertMatch(manifestContents, .and(.contains(".plugin("), .contains("capability: .buildTool()"))) + #expect(manifestContents.contains(".plugin(") && manifestContents.contains("targets: [\"MyBuildToolPlugin\"]")) + #expect(manifestContents.contains(".plugin(") && manifestContents.contains("capability: .buildTool()")) // Check basic content that we expect in the plugin source file let source = path.appending("Plugins", "MyBuildToolPlugin.swift") - XCTAssertFileExists(source) + expectFileExists(at: source) let sourceContents: String = try localFileSystem.readFileContents(source) - XCTAssertMatch(sourceContents, .contains("struct MyBuildToolPlugin: BuildToolPlugin")) - XCTAssertMatch(sourceContents, .contains("createBuildCommands(context: PluginContext")) - XCTAssertMatch(sourceContents, .contains("import XcodeProjectPlugin")) - XCTAssertMatch(sourceContents, .contains("extension MyBuildToolPlugin: XcodeBuildToolPlugin")) - XCTAssertMatch(sourceContents, .contains("createBuildCommands(context: XcodePluginContext")) - } - } - - // MARK: Special case testing - - func testInitPackageNonc99Directory() async throws { - try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in - XCTAssertDirectoryExists(tempDirPath) - - // Create a directory with non c99name. - let packageRoot = tempDirPath.appending("some-package") - let packageName = packageRoot.basename - try localFileSystem.createDirectory(packageRoot) - XCTAssertDirectoryExists(packageRoot) - - // Create the package - let initPackage = try InitPackage( - name: packageName, - packageType: .library, - destinationPath: packageRoot, - fileSystem: localFileSystem - ) - initPackage.progressReporter = { message in } - try initPackage.writePackageStructure() - - // Try building it. - await XCTAssertBuilds( - packageRoot, - buildSystem: .native, - ) - let triple = try UserToolchain.default.targetTriple - XCTAssertFileExists(packageRoot.appending(components: ".build", triple.platformBuildPathComponent, "debug", "Modules", "some_package.swiftmodule")) - } - } - - func testNonC99NameExecutablePackage() async throws { - try await withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in - XCTAssertDirectoryExists(tempDirPath) - - let packageRoot = tempDirPath.appending("Foo") - try localFileSystem.createDirectory(packageRoot) - XCTAssertDirectoryExists(packageRoot) - - // Create package with non c99name. - let initPackage = try InitPackage( - name: "package-name", - packageType: .executable, - destinationPath: packageRoot, - fileSystem: localFileSystem - ) - try initPackage.writePackageStructure() - - await XCTAssertBuilds( - packageRoot, - buildSystem: .native, - ) + #expect(sourceContents.contains("struct MyBuildToolPlugin: BuildToolPlugin")) + #expect(sourceContents.contains("createBuildCommands(context: PluginContext")) + #expect(sourceContents.contains("import XcodeProjectPlugin")) + #expect(sourceContents.contains("extension MyBuildToolPlugin: XcodeBuildToolPlugin")) + #expect(sourceContents.contains("createBuildCommands(context: XcodePluginContext")) } } - func testPlatforms() throws { + @Test( + .tags( + .TestSize.medium, + ), + ) + func platforms() throws { try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in var options = InitPackage.InitPackageOptions(packageType: .library, supportedTestingLibraries: []) options.platforms = [ @@ -506,12 +654,17 @@ final class InitTests: XCTestCase { try initPackage.writePackageStructure() let contents: String = try localFileSystem.readFileContents(packageRoot.appending("Package.swift")) - XCTAssertMatch(contents, .contains(#"platforms: [.macOS(.v10_15), .iOS(.v12), .watchOS("2.1"), .tvOS("999.0")],"#)) + #expect(contents.contains(#"platforms: [.macOS(.v10_15), .iOS(.v12), .watchOS("2.1"), .tvOS("999.0")],"#)) } } - func testInitPackageIncludesSwiftLanguageMode() throws { - try testWithTemporaryDirectory { tmpPath in + @Test( + .tags( + .TestSize.medium, + ), + ) + func initPackageIncludesSwiftLanguageMode() throws { + try withTemporaryDirectory { tmpPath in let fs = localFileSystem let path = tmpPath.appending("testInitPackageIncludesSwiftLanguageMode") let name = path.basename @@ -531,7 +684,7 @@ final class InitTests: XCTestCase { // Verify the manifest includes Swift language mode let manifest = path.appending("Package.swift") let manifestContents: String = try localFileSystem.readFileContents(manifest) - XCTAssertMatch(manifestContents, .contains("swiftLanguageModes: [.v6]")) + #expect(manifestContents.contains("swiftLanguageModes: [.v6]")) } }