Skip to content

Tests: Migrate 3 suites to Swift Testing #9017

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.build
.test
.index-build
DerivedData
/.previous-build
Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ let package = Package(
name: "_InternalTestSupport",
dependencies: [
"Basics",
"DriverSupport",
"PackageFingerprint",
"PackageGraph",
"PackageLoading",
Expand Down
2 changes: 1 addition & 1 deletion Sources/PackageModel/Toolchain+SupportedFeatures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public enum SwiftCompilerFeature {
}

extension Toolchain {
public var supportesSupportedFeatures: Bool {
public var supportsSupportedFeatures: Bool {
guard let features = try? swiftCompilerSupportedFeatures else {
return false
}
Expand Down
25 changes: 25 additions & 0 deletions Sources/_InternalTestSupport/BuildConfiguration+Helpers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 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 enum PackageModel.BuildConfiguration

extension BuildConfiguration {

public var buildFor: String {
switch self {
case .debug:
return "debugging"
case .release:
return "production"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//

import struct SPMBuildCore.BuildSystemProvider

import enum PackageModel.BuildConfiguration

public var SupportedBuildSystemOnAllPlatforms: [BuildSystemProvider.Kind] = BuildSystemProvider.Kind.allCases.filter { $0 != .xcode }

Expand All @@ -22,3 +22,16 @@ public var SupportedBuildSystemOnPlatform: [BuildSystemProvider.Kind] {
SupportedBuildSystemOnAllPlatforms
#endif
}

public struct BuildData {
public let buildSystem: BuildSystemProvider.Kind
public let config: BuildConfiguration
}

public func getBuildData(for buildSystems: [BuildSystemProvider.Kind]) -> [BuildData] {
buildSystems.flatMap { buildSystem in
BuildConfiguration.allCases.compactMap { config in
return BuildData(buildSystem: buildSystem, config: config)
}
}
}
3 changes: 1 addition & 2 deletions Sources/_InternalTestSupport/SwiftPMProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,12 @@ extension SwiftPM {

// Unset the internal env variable that allows skipping certain tests.
environment["_SWIFTPM_SKIP_TESTS_LIST"] = nil
environment["SWIFTPM_EXEC_NAME"] = self.executableName

for (key, value) in env ?? [:] {
environment[key] = value
}

var completeArgs = [xctestBinaryPath.pathString]
var completeArgs = [Self.xctestBinaryPath(for: RelativePath(self.executableName)).pathString]
if let packagePath = packagePath {
completeArgs += ["--package-path", packagePath.pathString]
}
Expand Down
67 changes: 64 additions & 3 deletions Sources/_InternalTestSupport/SwiftTesting+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,63 @@ public func expectFileExists(
)
}

public func expectFileDoesNotExists(
at fixturePath: AbsolutePath,
_ comment: Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
) {
let commentPrefix =
if let comment {
"\(comment): "
} else {
""
}
#expect(
!localFileSystem.exists(fixturePath),
"\(commentPrefix)\(fixturePath) does not exist",
sourceLocation: sourceLocation,
)
}

public func expectFileIsExecutable(
at fixturePath: AbsolutePath,
_ comment: Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
) {
let commentPrefix =
if let comment {
"\(comment): "
} else {
""
}
#expect(
localFileSystem.isExecutableFile(fixturePath),
"\(commentPrefix)\(fixturePath) does not exist",
sourceLocation: sourceLocation,
)
}

public func expectDirectoryExists(
at path: AbsolutePath,
sourceLocation: SourceLocation = #_sourceLocation,
) {
#expect(
localFileSystem.isDirectory(path),
"Expected directory doesn't exist: \(path)",
sourceLocation: sourceLocation,
)
}

public func expectDirectoryDoesNotExist(
at path: AbsolutePath,
sourceLocation: SourceLocation = #_sourceLocation,
) {
#expect(
!localFileSystem.isDirectory(path),
"Directory exists unexpectedly: \(path)",
sourceLocation: sourceLocation,
)
}

public func expectThrowsCommandExecutionError<T>(
_ expression: @autoclosure () async throws -> T,
Expand All @@ -32,8 +89,9 @@ public func expectThrowsCommandExecutionError<T>(
) async {
await expectAsyncThrowsError(try await expression(), message(), sourceLocation: sourceLocation) { error in
guard case SwiftPMError.executionFailure(let processError, let stdout, let stderr) = error,
case AsyncProcessResult.Error.nonZeroExit(let processResult) = processError,
processResult.exitStatus != .terminated(code: 0) else {
case AsyncProcessResult.Error.nonZeroExit(let processResult) = processError,
processResult.exitStatus != .terminated(code: 0)
else {
Issue.record("Unexpected error type: \(error.interpolationDescription)", sourceLocation: sourceLocation)
return
}
Expand All @@ -50,7 +108,10 @@ public func expectAsyncThrowsError<T>(
) async {
do {
_ = try await expression()
Issue.record(message() ?? "Expected an error, which did not not.", sourceLocation: sourceLocation)
Issue.record(
message() ?? "Expected an error, which did not occur.",
sourceLocation: sourceLocation,
)
} catch {
errorHandler(error)
}
Expand Down
38 changes: 36 additions & 2 deletions Sources/_InternalTestSupport/SwiftTesting+Tags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ extension Tag.TestSize {
extension Tag.Feature {
public enum Command {}
public enum PackageType {}
public enum ProductType {}
public enum TargetType {}

@Tag public static var CodeCoverage: Tag
@Tag public static var Mirror: Tag
@Tag public static var NetRc: Tag
@Tag public static var Resource: Tag
@Tag public static var SpecialCharacters: Tag
@Tag public static var Traits: Tag
Expand All @@ -38,19 +41,37 @@ extension Tag.Feature.Command {
public enum Package {}
public enum PackageRegistry {}
@Tag public static var Build: Tag
@Tag public static var Test: Tag
@Tag public static var Run: Tag
@Tag public static var Sdk: Tag
@Tag public static var Test: Tag
}

extension Tag.Feature.Command.Package {
@Tag public static var General: Tag
@Tag public static var AddDependency: Tag
@Tag public static var AddProduct: Tag
@Tag public static var ArchiveSource: Tag
@Tag public static var AddSetting: Tag
@Tag public static var AddTarget: Tag
@Tag public static var AddTargetDependency: Tag
@Tag public static var BuildPlugin: Tag
@Tag public static var Clean: Tag
@Tag public static var CommandPlugin: Tag
@Tag public static var CompletionTool: Tag
@Tag public static var Config: Tag
@Tag public static var Init: Tag
@Tag public static var Describe: Tag
@Tag public static var DumpPackage: Tag
@Tag public static var DumpSymbolGraph: Tag
@Tag public static var Edit: Tag
@Tag public static var Init: Tag
@Tag public static var Migrate: Tag
@Tag public static var Plugin: Tag
@Tag public static var Reset: Tag
@Tag public static var Resolve: Tag
@Tag public static var ShowDependencies: Tag
@Tag public static var ShowExecutables: Tag
@Tag public static var ToolsVersion: Tag
@Tag public static var Unedit: Tag
@Tag public static var Update: Tag
}

Expand All @@ -63,6 +84,19 @@ extension Tag.Feature.Command.PackageRegistry {
@Tag public static var Unset: Tag
}

extension Tag.Feature.TargetType {
@Tag public static var Executable: Tag
@Tag public static var Library: Tag
@Tag public static var Macro: Tag
}

extension Tag.Feature.ProductType {
@Tag public static var DynamicLibrary: Tag
@Tag public static var Executable: Tag
@Tag public static var Library: Tag
@Tag public static var Plugin: Tag
@Tag public static var StaticLibrary: Tag
}
extension Tag.Feature.PackageType {
@Tag public static var Library: Tag
@Tag public static var Executable: Tag
Expand Down
95 changes: 80 additions & 15 deletions Sources/_InternalTestSupport/SwiftTesting+TraitConditional.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/*
This source file is part of the Swift.org open source project

Expand All @@ -15,7 +14,7 @@ import class PackageModel.UserToolchain
import DriverSupport
import Basics
import Testing
import TSCclibc // for SPM_posix_spawn_file_actions_addchdir_np_supported
import TSCclibc // for SPM_posix_spawn_file_actions_addchdir_np_supported

extension Trait where Self == Testing.ConditionTrait {
/// Skip test if the host operating system does not match the running OS.
Expand All @@ -39,6 +38,72 @@ extension Trait where Self == Testing.ConditionTrait {
}
}

/// Enabled only if 'llvm-profdata' is available
public static var requiresLLVMProfData: Self {
disabled("skipping test because the `llvm-profdata` tool isn't available") {
let toolPath = try (try? UserToolchain.default)!.getLLVMProf()
return toolPath == nil
}
}

/// Enabled only if 'llvm-cov' is available
public static var requiresLLVMCov: Self {
disabled("skipping test because the `llvm-cov` tool isn't available") {
let toolPath = try (try? UserToolchain.default)!.getLLVMCov()
return toolPath == nil
}
}

/// Enabled only if 'swift-symbolgraph-extract' is available
public static var requiresSymbolgraphExtract: Self {
disabled("skipping test because the `swift-symbolgraph-extract` tools isn't available") {
let toolPath = try (try? UserToolchain.default)!.getSymbolGraphExtract()
return toolPath == nil
}
}

/// Enabled only is stdlib is supported by the toolchain
public static var requiresStdlibSupport: Self {
enabled("skipping because static stdlib is not supported by the toolchain") {
let args = try [
UserToolchain.default.swiftCompilerPath.pathString,
"-static-stdlib", "-emit-executable", "-o", "/dev/null", "-",
]
let process = AsyncProcess(arguments: args)
let stdin = try process.launch()
stdin.write(sequence: "".utf8)
try stdin.close()
let result = try await process.waitUntilExit()

return result.exitStatus == .terminated(code: 0)
}
}

// Enabled if the toolchain has supported features
public static var supportsSupportedFeatures: Self {
enabled("skipping because test environment compiler doesn't support `-print-supported-features`") {
(try? UserToolchain.default)!.supportsSupportedFeatures
}
}

/// Skip of the executable is not available
public static func requires(executable: String) -> Self {
let message: Comment?
let isToolAvailable: Bool
do {
try _requiresTools(executable)
isToolAvailable = true
message = nil
} catch (let AsyncProcessResult.Error.nonZeroExit(result)) {
isToolAvailable = false
message = "Skipping as tool \(executable) is not found in the path. (\(result.description))"
} catch {
isToolAvailable = false
message = "Skipping. Unable to determine if tool exists. Error: \(error) "
}
return enabled(if: isToolAvailable, message)
}

/// Enaled only if marcros are built as dylibs
public static var requiresBuildingMacrosAsDylibs: Self {
enabled("test is only supported if `BUILD_MACROS_AS_DYLIBS` is set") {
Expand Down Expand Up @@ -71,16 +136,16 @@ extension Trait where Self == Testing.ConditionTrait {
/// Ensure platform support working directory
public static var requiresWorkingDirectorySupport: Self {
enabled("working directory not supported on this platform") {
#if !os(Windows)
// needed for archiving
if SPM_posix_spawn_file_actions_addchdir_np_supported() {
#if !os(Windows)
// needed for archiving
if SPM_posix_spawn_file_actions_addchdir_np_supported() {
return true
} else {
return false
}
#else
return true
} else {
return false
}
#else
return true
#endif
#endif
}
}

Expand Down Expand Up @@ -118,9 +183,9 @@ extension Trait where Self == Testing.ConditionTrait {
public static func skipIfXcodeBuilt() -> Self {
disabled("Tests built by Xcode") {
#if Xcode
true
true
#else
false
false
#endif
}
}
Expand All @@ -129,9 +194,9 @@ extension Trait where Self == Testing.ConditionTrait {
public static var requireSwift6_2: Self {
enabled("This test requires Swift 6.2, or newer.") {
#if compiler(>=6.2)
true
true
#else
false
false
#endif
}
}
Expand Down
Loading