Skip to content

Commit 53116b5

Browse files
authored
Tests: Migrate to Swift Testing and augment suites (#8988)
To aid with the Swift Build integration, migrate a few test suites to Swift Testing and augment the tests to build against the SwiftBuild build system in addition to the various build configuration. Depends on: #8975 Relates to #8997
1 parent 20c5bd3 commit 53116b5

File tree

8 files changed

+563
-299
lines changed

8 files changed

+563
-299
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2025 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Basics
12+
import Testing
13+
14+
public func expectFileExists(
15+
at path: AbsolutePath,
16+
sourceLocation: SourceLocation = #_sourceLocation,
17+
) {
18+
19+
#expect(
20+
localFileSystem.exists(path),
21+
"Files '\(path)' does not exist.",
22+
sourceLocation: sourceLocation,
23+
)
24+
}
25+
26+
27+
public func expectThrowsCommandExecutionError<T>(
28+
_ expression: @autoclosure () async throws -> T,
29+
_ message: @autoclosure () -> Comment = "",
30+
sourceLocation: SourceLocation = #_sourceLocation,
31+
_ errorHandler: (_ error: CommandExecutionError) -> Void = { _ in }
32+
) async {
33+
await expectAsyncThrowsError(try await expression(), message(), sourceLocation: sourceLocation) { error in
34+
guard case SwiftPMError.executionFailure(let processError, let stdout, let stderr) = error,
35+
case AsyncProcessResult.Error.nonZeroExit(let processResult) = processError,
36+
processResult.exitStatus != .terminated(code: 0) else {
37+
Issue.record("Unexpected error type: \(error.interpolationDescription)", sourceLocation: sourceLocation)
38+
return
39+
}
40+
errorHandler(CommandExecutionError(result: processResult, stdout: stdout, stderr: stderr))
41+
}
42+
}
43+
44+
/// An `async`-friendly replacement for `XCTAssertThrowsError`.
45+
public func expectAsyncThrowsError<T>(
46+
_ expression: @autoclosure () async throws -> T,
47+
_ message: @autoclosure () -> Comment = "",
48+
sourceLocation: SourceLocation = #_sourceLocation,
49+
_ errorHandler: (_ error: any Error) -> Void = { _ in }
50+
) async {
51+
do {
52+
_ = try await expression()
53+
Issue.record(message(), sourceLocation: sourceLocation)
54+
} catch {
55+
errorHandler(error)
56+
}
57+
}

Sources/_InternalTestSupport/SwiftTesting+Tags.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extension Tag.Feature.Command.Package {
4444
@Tag public static var DumpPackage: Tag
4545
@Tag public static var DumpSymbolGraph: Tag
4646
@Tag public static var Plugin: Tag
47+
@Tag public static var Reset: Tag
48+
@Tag public static var ToolsVersion: Tag
4749
}
4850

4951
extension Tag.Feature.PackageType {

Sources/_InternalTestSupport/SwiftTesting+TraitConditional.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import class Foundation.FileManager
1313
import class Foundation.ProcessInfo
1414
import class PackageModel.UserToolchain
15+
import DriverSupport
1516
import Basics
1617
import Testing
1718

@@ -37,6 +38,35 @@ extension Trait where Self == Testing.ConditionTrait {
3738
}
3839
}
3940

41+
/// Enaled only if marcros are built as dylibs
42+
public static var requiresBuildingMacrosAsDylibs: Self {
43+
enabled("test is only supported if `BUILD_MACROS_AS_DYLIBS` is set") {
44+
#if BUILD_MACROS_AS_DYLIBS
45+
true
46+
#else
47+
false
48+
#endif
49+
}
50+
}
51+
52+
/// Check for required compiler support
53+
public static func requiresFrontEndFlags(flags: Set<String>) -> Self {
54+
enabled("test requires \(flags.joined(separator: ", "))") {
55+
try DriverSupport.checkSupportedFrontendFlags(flags: flags, toolchain: UserToolchain.default, fileSystem: localFileSystem)
56+
}
57+
}
58+
59+
private static func requiresHostLibrary(lib: String) -> Self {
60+
enabled("test requires `\(lib)` to exist in the host toolchain") {
61+
let libSwiftSyntaxMacrosPath = try UserToolchain.default.hostLibDir.appending("libSwiftSyntaxMacros.dylib")
62+
return localFileSystem.exists(libSwiftSyntaxMacrosPath)
63+
}
64+
}
65+
66+
public static var requiresSwiftTestingMacros: Self {
67+
requiresHostLibrary(lib: "libSwiftSyntaxMacros.dylib")
68+
}
69+
4070
/// Skip test unconditionally
4171
public static func skip(_ comment: Comment? = nil) -> Self {
4272
disabled(comment ?? "Unconditional skip, a comment should be added for the reason") { true }

Tests/FunctionalTests/MacroTests.swift

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,49 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2023 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2023-2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
99
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
import Foundation
13+
14+
import struct SPMBuildCore.BuildSystemProvider
1215

1316
import DriverSupport
1417
import _InternalTestSupport
1518
import PackageModel
16-
import XCTest
17-
18-
class MacroTests: XCTestCase {
19-
func testMacrosBasic() throws {
20-
#if BUILD_MACROS_AS_DYLIBS
21-
// Check for required compiler support.
22-
try XCTSkipIf(!DriverSupport.checkSupportedFrontendFlags(flags: ["load-plugin-library"], toolchain: UserToolchain.default, fileSystem: localFileSystem), "test needs `-load-plugin-library`")
23-
24-
// Check for presence of `libSwiftSyntaxMacros`.
25-
let libSwiftSyntaxMacrosPath = try UserToolchain.default.hostLibDir.appending("libSwiftSyntaxMacros.dylib")
26-
try XCTSkipIf(!localFileSystem.exists(libSwiftSyntaxMacrosPath), "test need `libSwiftSyntaxMacros` to exist in the host toolchain")
19+
import Testing
2720

28-
try fixtureXCTest(name: "Macros") { fixturePath in
29-
let (stdout, _) = try executeSwiftBuild(
21+
@Suite(
22+
.tags(
23+
Tag.TestSize.large
24+
),
25+
)
26+
struct MacroTests {
27+
@Test(
28+
.requiresBuildingMacrosAsDylibs,
29+
.requiresFrontEndFlags(flags: ["load-plugin-library"]),
30+
.requiresSwiftTestingMacros,
31+
.tags(
32+
Tag.Feature.Command.Build
33+
),
34+
arguments: SupportedBuildSystemOnAllPlatforms, BuildConfiguration.allCases,
35+
)
36+
func macrosBasic(
37+
buildSystem: BuildSystemProvider.Kind,
38+
configuration: BuildConfiguration,
39+
) async throws {
40+
try await fixture(name: "Macros") { fixturePath in
41+
let (stdout, _) = try await executeSwiftBuild(
3042
fixturePath.appending("MacroPackage"),
31-
configuration: .debug,
32-
buildSystem: .native,
43+
configuration: configuration,
44+
buildSystem: buildSystem,
3345
)
34-
XCTAssert(stdout.contains("@__swiftmacro_11MacroClient11fontLiteralfMf_.swift as Font"), "stdout:\n\(stdout)")
35-
XCTAssert(stdout.contains("Build complete!"), "stdout:\n\(stdout)")
46+
#expect(stdout.contains("@__swiftmacro_11MacroClient11fontLiteralfMf_.swift as Font"), "stdout:\n\(stdout)")
47+
#expect(stdout.contains("Build complete!"), "stdout:\n\(stdout)")
3648
}
37-
#else
38-
try XCTSkipIf(true, "test is only supported if `BUILD_MACROS_AS_DYLIBS`")
39-
#endif
4049
}
4150
}

0 commit comments

Comments
 (0)