Skip to content

Commit 0d48878

Browse files
committed
[SWT-NNNN] Exit tests
One of the first enhancement requests we received for swift-testing was the ability to test for precondition failures and other critical failures that terminate the current process when they occur. This feature is also frequently requested for XCTest. With swift-testing, we have the opportunity to build such a feature in an ergonomic way. Read the full proposal [here](https://github.com/apple/swift-testing/blob/jgrynspan/exit-tests-proposal/Documentation/Proposals/NNNN-exit-tests.md).
1 parent 19329a4 commit 0d48878

24 files changed

+1003
-16
lines changed

Documentation/Proposals/NNNN-exit-tests.md

+723
Large diffs are not rendered by default.

Sources/Testing/ExitTests/ExitCondition.swift

+27-6
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,34 @@ private import _TestingInternals
1717
/// test is expected to pass or fail by passing them to
1818
/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or
1919
/// ``require(exitsWith:observing:_:sourceLocation:performing:)``.
20-
@_spi(Experimental)
20+
///
21+
/// ## Topics
22+
///
23+
/// ### Successful exit conditions
24+
///
25+
/// - ``success``
26+
///
27+
/// ### Failing exit conditions
28+
///
29+
/// - ``failure``
30+
/// - ``exitCode(_:)``
31+
/// - ``signal(_:)``
32+
///
33+
/// ### Comparing exit conditions
34+
///
35+
/// - ``/Swift/Optional/==(_:_:)``
36+
/// - ``/Swift/Optional/!=(_:_:)``
37+
/// - ``/Swift/Optional/===(_:_:)``
38+
/// - ``/Swift/Optional/!==(_:_:)``
2139
#if SWT_NO_PROCESS_SPAWNING
2240
@available(*, unavailable, message: "Exit tests are not available on this platform.")
2341
#endif
2442
public enum ExitCondition: Sendable {
2543
/// The process terminated successfully with status `EXIT_SUCCESS`.
44+
///
45+
/// The C programming language defines two [standard exit codes](https://en.cppreference.com/w/c/program/EXIT_status),
46+
/// `EXIT_SUCCESS` and `EXIT_FAILURE` as well as `0` (as a synonym for
47+
/// `EXIT_SUCCESS`.)
2648
public static var success: Self {
2749
// Strictly speaking, the C standard treats 0 as a successful exit code and
2850
// potentially distinct from EXIT_SUCCESS. To my knowledge, no modern
@@ -76,7 +98,6 @@ public enum ExitCondition: Sendable {
7698

7799
// MARK: - Equatable
78100

79-
@_spi(Experimental)
80101
#if SWT_NO_PROCESS_SPAWNING
81102
@available(*, unavailable, message: "Exit tests are not available on this platform.")
82103
#endif
@@ -107,7 +128,7 @@ extension Optional<ExitCondition> {
107128
/// or [`Hashable`](https://developer.apple.com/documentation/swift/hashable).
108129
///
109130
/// For any values `a` and `b`, `a == b` implies that `a != b` is `false`.
110-
public static func ==(lhs: Self, rhs: Self) -> Bool {
131+
public static func ==(lhs: ExitCondition?, rhs: ExitCondition?) -> Bool {
111132
#if !SWT_NO_PROCESS_SPAWNING
112133
return switch (lhs, rhs) {
113134
case let (.failure, .exitCode(exitCode)), let (.exitCode(exitCode), .failure):
@@ -149,7 +170,7 @@ extension Optional<ExitCondition> {
149170
/// or [`Hashable`](https://developer.apple.com/documentation/swift/hashable).
150171
///
151172
/// For any values `a` and `b`, `a == b` implies that `a != b` is `false`.
152-
public static func !=(lhs: Self, rhs: Self) -> Bool {
173+
public static func !=(lhs: ExitCondition?, rhs: ExitCondition?) -> Bool {
153174
#if !SWT_NO_PROCESS_SPAWNING
154175
!(lhs == rhs)
155176
#else
@@ -183,7 +204,7 @@ extension Optional<ExitCondition> {
183204
/// or [`Hashable`](https://developer.apple.com/documentation/swift/hashable).
184205
///
185206
/// For any values `a` and `b`, `a === b` implies that `a !== b` is `false`.
186-
public static func ===(lhs: Self, rhs: Self) -> Bool {
207+
public static func ===(lhs: ExitCondition?, rhs: ExitCondition?) -> Bool {
187208
return switch (lhs, rhs) {
188209
case (.none, .none):
189210
true
@@ -224,7 +245,7 @@ extension Optional<ExitCondition> {
224245
/// or [`Hashable`](https://developer.apple.com/documentation/swift/hashable).
225246
///
226247
/// For any values `a` and `b`, `a === b` implies that `a !== b` is `false`.
227-
public static func !==(lhs: Self, rhs: Self) -> Bool {
248+
public static func !==(lhs: ExitCondition?, rhs: ExitCondition?) -> Bool {
228249
#if !SWT_NO_PROCESS_SPAWNING
229250
!(lhs === rhs)
230251
#else

Sources/Testing/ExitTests/ExitTest.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ private import _TestingInternals
2323
///
2424
/// Instances of this type describe an exit test defined by the test author and
2525
/// discovered or called at runtime.
26-
@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
26+
@_spi(ForToolsIntegrationOnly)
2727
#if SWT_NO_EXIT_TESTS
2828
@available(*, unavailable, message: "Exit tests are not available on this platform.")
2929
#endif
@@ -170,7 +170,6 @@ extension ExitTest {
170170
/// - Warning: This protocol is used to implement the `#expect(exitsWith:)`
171171
/// macro. Do not use it directly.
172172
@_alwaysEmitConformanceMetadata
173-
@_spi(Experimental)
174173
public protocol __ExitTestContainer {
175174
/// The expected exit condition of the exit test.
176175
static var __expectedExitCondition: ExitCondition { get }

Sources/Testing/ExitTests/ExitTestArtifacts.swift

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
/// instances of this type.
1717
///
1818
/// - Warning: The name of this type is still unstable and subject to change.
19-
@_spi(Experimental)
2019
#if SWT_NO_EXIT_TESTS
2120
@available(*, unavailable, message: "Exit tests are not available on this platform.")
2221
#endif

Sources/Testing/Expectations/Expectation+Macro.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,12 @@ public macro require<R>(
511511
/// ```
512512
///
513513
/// An exit test cannot run within another exit test.
514-
@_spi(Experimental)
515514
#if SWT_NO_EXIT_TESTS
516515
@available(*, unavailable, message: "Exit tests are not available on this platform.")
517516
#endif
518517
@discardableResult
519-
@freestanding(expression) public macro expect(
518+
@freestanding(expression)
519+
public macro expect(
520520
exitsWith expectedExitCondition: ExitCondition,
521521
observing observedValues: [any PartialKeyPath<ExitTestArtifacts> & Sendable] = [],
522522
_ comment: @autoclosure () -> Comment? = nil,
@@ -623,12 +623,12 @@ public macro require<R>(
623623
/// ```
624624
///
625625
/// An exit test cannot run within another exit test.
626-
@_spi(Experimental)
627626
#if SWT_NO_EXIT_TESTS
628627
@available(*, unavailable, message: "Exit tests are not available on this platform.")
629628
#endif
630629
@discardableResult
631-
@freestanding(expression) public macro require(
630+
@freestanding(expression)
631+
public macro require(
632632
exitsWith expectedExitCondition: ExitCondition,
633633
observing observedValues: [any PartialKeyPath<ExitTestArtifacts> & Sendable] = [],
634634
_ comment: @autoclosure () -> Comment? = nil,

Sources/Testing/Expectations/ExpectationChecking+Macro.swift

-1
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,6 @@ public func __checkClosureCall<R>(
11391139
///
11401140
/// - Warning: This function is used to implement the `#expect()` and
11411141
/// `#require()` macros. Do not call it directly.
1142-
@_spi(Experimental)
11431142
public func __checkClosureCall(
11441143
exitsWith expectedExitCondition: ExitCondition,
11451144
observing observedValues: [any PartialKeyPath<ExitTestArtifacts> & Sendable],

Sources/Testing/Running/Configuration.swift

-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ public struct Configuration: Sendable {
198198
/// When using the `swift test` command from Swift Package Manager, this
199199
/// property is pre-configured. Otherwise, the default value of this property
200200
/// records an issue indicating that it has not been configured.
201-
@_spi(Experimental)
202201
public var exitTestHandler: ExitTest.Handler = { _ in
203202
throw SystemError(description: "Exit test support has not been implemented by the current testing infrastructure.")
204203
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitCondition``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``/Swift/Optional/!=(_:_:)``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``/Swift/Optional/!==(_:_:)``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``/Swift/Optional/==(_:_:)``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``/Swift/Optional/===(_:_:)``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitCondition/exitCode(_:)``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitCondition/failure``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitCondition/signal(_:)``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitCondition/success``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitTestArtifacts``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitTestArtifacts/exitCondition``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitTestArtifacts/standardErrorContent``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/ExitTestArtifacts/standardOutputContent``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ``Testing/expect(exitsWith:observing:_:sourceLocation:performing:)``
2+
3+
<!--
4+
This source file is part of the Swift.org open source project
5+
6+
Copyright (c) 2024 Apple Inc. and the Swift project authors
7+
Licensed under Apache License v2.0 with Runtime Library Exception
8+
9+
See https://swift.org/LICENSE.txt for license information
10+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
-->
12+
13+
@Metadata {
14+
@Available(Xcode, introduced: 999.0)
15+
@Available(Swift, introduced: 999.0)
16+
}

0 commit comments

Comments
 (0)