Skip to content

Commit 099d177

Browse files
Promote exit tests to API (#324)
This PR promotes exit tests to API, pending approval of the proposal at swiftlang/swift-evolution#2718. View the full proposal [here](https://github.com/grynspan/swift-evolution/blob/jgrynspan/swift-testing-exit-tests/proposals/testing/NNNN-exit-tests.md). ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated. --------- Co-authored-by: Stuart Montgomery <[email protected]>
1 parent 7f0016f commit 099d177

23 files changed

+478
-374
lines changed

Sources/Testing/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ add_library(Testing
3636
ExitTests/ExitTest.Condition.swift
3737
ExitTests/ExitTest.Result.swift
3838
ExitTests/SpawnProcess.swift
39-
ExitTests/StatusAtExit.swift
39+
ExitTests/ExitStatus.swift
4040
ExitTests/WaitFor.swift
4141
Expectations/Expectation.swift
4242
Expectations/Expectation+Macro.swift

Sources/Testing/ExitTests/StatusAtExit.swift renamed to Sources/Testing/ExitTests/ExitStatus.swift

+40-20
Original file line numberDiff line numberDiff line change
@@ -10,74 +10,94 @@
1010

1111
private import _TestingInternals
1212

13-
/// An enumeration describing possible status a process will yield on exit.
13+
/// An enumeration describing possible status a process will report on exit.
1414
///
1515
/// You can convert an instance of this type to an instance of
1616
/// ``ExitTest/Condition`` using ``ExitTest/Condition/init(_:)``. That value
1717
/// can then be used to describe the condition under which an exit test is
1818
/// expected to pass or fail by passing it to
19-
/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or
20-
/// ``require(exitsWith:observing:_:sourceLocation:performing:)``.
21-
@_spi(Experimental)
19+
/// ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` or
20+
/// ``require(processExitsWith:observing:_:sourceLocation:performing:)``.
21+
///
22+
/// @Metadata {
23+
/// @Available(Swift, introduced: 6.2)
24+
/// }
2225
#if SWT_NO_PROCESS_SPAWNING
2326
@available(*, unavailable, message: "Exit tests are not available on this platform.")
2427
#endif
25-
public enum StatusAtExit: Sendable {
26-
/// The process terminated with the given exit code.
28+
public enum ExitStatus: Sendable {
29+
/// The process exited with the given exit code.
2730
///
2831
/// - Parameters:
29-
/// - exitCode: The exit code yielded by the process.
32+
/// - exitCode: The exit code reported by the process.
3033
///
31-
/// The C programming language defines two [standard exit codes](https://en.cppreference.com/w/c/program/EXIT_status),
32-
/// `EXIT_SUCCESS` and `EXIT_FAILURE`. Platforms may additionally define their
33-
/// own non-standard exit codes:
34+
/// The C programming language defines two standard exit codes, `EXIT_SUCCESS`
35+
/// and `EXIT_FAILURE`. Platforms may additionally define their own
36+
/// non-standard exit codes:
3437
///
3538
/// | Platform | Header |
3639
/// |-|-|
3740
/// | macOS | [`<stdlib.h>`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/_Exit.3.html), [`<sysexits.h>`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysexits.3.html) |
38-
/// | Linux | [`<stdlib.h>`](https://sourceware.org/glibc/manual/latest/html_node/Exit-Status.html), `<sysexits.h>` |
41+
/// | Linux | [`<stdlib.h>`](https://www.kernel.org/doc/man-pages/online/pages/man3/exit.3.html), [`<sysexits.h>`](https://www.kernel.org/doc/man-pages/online/pages/man3/sysexits.h.3head.html) |
3942
/// | FreeBSD | [`<stdlib.h>`](https://man.freebsd.org/cgi/man.cgi?exit(3)), [`<sysexits.h>`](https://man.freebsd.org/cgi/man.cgi?sysexits(3)) |
4043
/// | OpenBSD | [`<stdlib.h>`](https://man.openbsd.org/exit.3), [`<sysexits.h>`](https://man.openbsd.org/sysexits.3) |
4144
/// | Windows | [`<stdlib.h>`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure) |
4245
///
46+
/// @Comment {
47+
/// See https://en.cppreference.com/w/c/program/EXIT_status for more
48+
/// information about exit codes defined by the C standard.
49+
/// }
50+
///
4351
/// On macOS, FreeBSD, OpenBSD, and Windows, the full exit code reported by
44-
/// the process is yielded to the parent process. Linux and other POSIX-like
52+
/// the process is reported to the parent process. Linux and other POSIX-like
4553
/// systems may only reliably report the low unsigned 8 bits (0&ndash;255) of
4654
/// the exit code.
55+
///
56+
/// @Metadata {
57+
/// @Available(Swift, introduced: 6.2)
58+
/// }
4759
case exitCode(_ exitCode: CInt)
4860

49-
/// The process terminated with the given signal.
61+
/// The process exited with the given signal.
5062
///
5163
/// - Parameters:
52-
/// - signal: The signal that terminated the process.
64+
/// - signal: The signal that caused the process to exit.
5365
///
54-
/// The C programming language defines a number of [standard signals](https://en.cppreference.com/w/c/program/SIG_types).
55-
/// Platforms may additionally define their own non-standard signal codes:
66+
/// The C programming language defines a number of standard signals. Platforms
67+
/// may additionally define their own non-standard signal codes:
5668
///
5769
/// | Platform | Header |
5870
/// |-|-|
5971
/// | macOS | [`<signal.h>`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/signal.3.html) |
60-
/// | Linux | [`<signal.h>`](https://sourceware.org/glibc/manual/latest/html_node/Standard-Signals.html) |
72+
/// | Linux | [`<signal.h>`](https://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html) |
6173
/// | FreeBSD | [`<signal.h>`](https://man.freebsd.org/cgi/man.cgi?signal(3)) |
6274
/// | OpenBSD | [`<signal.h>`](https://man.openbsd.org/signal.3) |
6375
/// | Windows | [`<signal.h>`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/signal-constants) |
76+
///
77+
/// @Comment {
78+
/// See https://en.cppreference.com/w/c/program/SIG_types for more
79+
/// information about signals defined by the C standard.
80+
/// }
81+
///
82+
/// @Metadata {
83+
/// @Available(Swift, introduced: 6.2)
84+
/// }
6485
case signal(_ signal: CInt)
6586
}
6687

6788
// MARK: - Equatable
6889

69-
@_spi(Experimental)
7090
#if SWT_NO_PROCESS_SPAWNING
7191
@available(*, unavailable, message: "Exit tests are not available on this platform.")
7292
#endif
73-
extension StatusAtExit: Equatable {}
93+
extension ExitStatus: Equatable {}
7494

7595
// MARK: - CustomStringConvertible
7696
@_spi(Experimental)
7797
#if SWT_NO_PROCESS_SPAWNING
7898
@available(*, unavailable, message: "Exit tests are not available on this platform.")
7999
#endif
80-
extension StatusAtExit: CustomStringConvertible {
100+
extension ExitStatus: CustomStringConvertible {
81101
public var description: String {
82102
switch self {
83103
case let .exitCode(exitCode):

Sources/Testing/ExitTests/ExitTest.CapturedValue.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension ExitTest {
2626
/// exit test:
2727
///
2828
/// ```swift
29-
/// await #expect(exitsWith: .failure) { [a = a as T, b = b as U, c = c as V] in
29+
/// await #expect(processExitsWith: .failure) { [a = a as T, b = b as U, c = c as V] in
3030
/// ...
3131
/// }
3232
/// ```

Sources/Testing/ExitTests/ExitTest.Condition.swift

+82-31
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
private import _TestingInternals
1212

13-
@_spi(Experimental)
1413
#if SWT_NO_EXIT_TESTS
1514
@available(*, unavailable, message: "Exit tests are not available on this platform.")
1615
#endif
@@ -19,13 +18,29 @@ extension ExitTest {
1918
///
2019
/// Values of this type are used to describe the conditions under which an
2120
/// exit test is expected to pass or fail by passing them to
22-
/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or
23-
/// ``require(exitsWith:observing:_:sourceLocation:performing:)``.
21+
/// ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` or
22+
/// ``require(processExitsWith:observing:_:sourceLocation:performing:)``.
23+
///
24+
/// ## Topics
25+
///
26+
/// ### Successful exit conditions
27+
///
28+
/// - ``success``
29+
///
30+
/// ### Failing exit conditions
31+
///
32+
/// - ``failure``
33+
/// - ``exitCode(_:)``
34+
/// - ``signal(_:)``
35+
///
36+
/// @Metadata {
37+
/// @Available(Swift, introduced: 6.2)
38+
/// }
2439
public struct Condition: Sendable {
2540
/// An enumeration describing the possible conditions for an exit test.
2641
private enum _Kind: Sendable, Equatable {
2742
/// The exit test must exit with a particular exit status.
28-
case statusAtExit(StatusAtExit)
43+
case exitStatus(ExitStatus)
2944

3045
/// The exit test must exit successfully.
3146
case success
@@ -41,49 +56,77 @@ extension ExitTest {
4156

4257
// MARK: -
4358

44-
@_spi(Experimental)
4559
#if SWT_NO_EXIT_TESTS
4660
@available(*, unavailable, message: "Exit tests are not available on this platform.")
4761
#endif
4862
extension ExitTest.Condition {
49-
/// A condition that matches when a process terminates successfully with exit
50-
/// code `EXIT_SUCCESS`.
63+
/// A condition that matches when a process exits normally.
64+
///
65+
/// This condition matches the exit code `EXIT_SUCCESS`.
66+
///
67+
/// @Metadata {
68+
/// @Available(Swift, introduced: 6.2)
69+
/// }
5170
public static var success: Self {
5271
Self(_kind: .success)
5372
}
5473

55-
/// A condition that matches when a process terminates abnormally with any
56-
/// exit code other than `EXIT_SUCCESS` or with any signal.
74+
/// A condition that matches when a process exits abnormally
75+
///
76+
/// This condition matches any exit code other than `EXIT_SUCCESS` or any
77+
/// signal that causes the process to exit.
78+
///
79+
/// @Metadata {
80+
/// @Available(Swift, introduced: 6.2)
81+
/// }
5782
public static var failure: Self {
5883
Self(_kind: .failure)
5984
}
6085

61-
public init(_ statusAtExit: StatusAtExit) {
62-
self.init(_kind: .statusAtExit(statusAtExit))
86+
/// Initialize an instance of this type that matches the specified exit
87+
/// status.
88+
///
89+
/// - Parameters:
90+
/// - exitStatus: The particular exit status this condition should match.
91+
///
92+
/// @Metadata {
93+
/// @Available(Swift, introduced: 6.2)
94+
/// }
95+
public init(_ exitStatus: ExitStatus) {
96+
self.init(_kind: .exitStatus(exitStatus))
6397
}
6498

6599
/// Creates a condition that matches when a process terminates with a given
66100
/// exit code.
67101
///
68102
/// - Parameters:
69-
/// - exitCode: The exit code yielded by the process.
103+
/// - exitCode: The exit code reported by the process.
70104
///
71-
/// The C programming language defines two [standard exit codes](https://en.cppreference.com/w/c/program/EXIT_status),
72-
/// `EXIT_SUCCESS` and `EXIT_FAILURE`. Platforms may additionally define their
73-
/// own non-standard exit codes:
105+
/// The C programming language defines two standard exit codes, `EXIT_SUCCESS`
106+
/// and `EXIT_FAILURE`. Platforms may additionally define their own
107+
/// non-standard exit codes:
74108
///
75109
/// | Platform | Header |
76110
/// |-|-|
77111
/// | macOS | [`<stdlib.h>`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/_Exit.3.html), [`<sysexits.h>`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysexits.3.html) |
78-
/// | Linux | [`<stdlib.h>`](https://sourceware.org/glibc/manual/latest/html_node/Exit-Status.html), `<sysexits.h>` |
112+
/// | Linux | [`<stdlib.h>`](https://www.kernel.org/doc/man-pages/online/pages/man3/exit.3.html), [`<sysexits.h>`](https://www.kernel.org/doc/man-pages/online/pages/man3/sysexits.h.3head.html) |
79113
/// | FreeBSD | [`<stdlib.h>`](https://man.freebsd.org/cgi/man.cgi?exit(3)), [`<sysexits.h>`](https://man.freebsd.org/cgi/man.cgi?sysexits(3)) |
80114
/// | OpenBSD | [`<stdlib.h>`](https://man.openbsd.org/exit.3), [`<sysexits.h>`](https://man.openbsd.org/sysexits.3) |
81115
/// | Windows | [`<stdlib.h>`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure) |
82116
///
117+
/// @Comment {
118+
/// See https://en.cppreference.com/w/c/program/EXIT_status for more
119+
/// information about exit codes defined by the C standard.
120+
/// }
121+
///
83122
/// On macOS, FreeBSD, OpenBSD, and Windows, the full exit code reported by
84-
/// the process is yielded to the parent process. Linux and other POSIX-like
123+
/// the process is reported to the parent process. Linux and other POSIX-like
85124
/// systems may only reliably report the low unsigned 8 bits (0&ndash;255) of
86125
/// the exit code.
126+
///
127+
/// @Metadata {
128+
/// @Available(Swift, introduced: 6.2)
129+
/// }
87130
public static func exitCode(_ exitCode: CInt) -> Self {
88131
#if !SWT_NO_EXIT_TESTS
89132
Self(.exitCode(exitCode))
@@ -92,22 +135,30 @@ extension ExitTest.Condition {
92135
#endif
93136
}
94137

95-
/// Creates a condition that matches when a process terminates with a given
96-
/// signal.
138+
/// Creates a condition that matches when a process exits with a given signal.
97139
///
98140
/// - Parameters:
99-
/// - signal: The signal that terminated the process.
141+
/// - signal: The signal that caused the process to exit.
100142
///
101-
/// The C programming language defines a number of [standard signals](https://en.cppreference.com/w/c/program/SIG_types).
102-
/// Platforms may additionally define their own non-standard signal codes:
143+
/// The C programming language defines a number of standard signals. Platforms
144+
/// may additionally define their own non-standard signal codes:
103145
///
104146
/// | Platform | Header |
105147
/// |-|-|
106148
/// | macOS | [`<signal.h>`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/signal.3.html) |
107-
/// | Linux | [`<signal.h>`](https://sourceware.org/glibc/manual/latest/html_node/Standard-Signals.html) |
149+
/// | Linux | [`<signal.h>`](https://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html) |
108150
/// | FreeBSD | [`<signal.h>`](https://man.freebsd.org/cgi/man.cgi?signal(3)) |
109151
/// | OpenBSD | [`<signal.h>`](https://man.openbsd.org/signal.3) |
110152
/// | Windows | [`<signal.h>`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/signal-constants) |
153+
///
154+
/// @Comment {
155+
/// See https://en.cppreference.com/w/c/program/SIG_types for more
156+
/// information about signals defined by the C standard.
157+
/// }
158+
///
159+
/// @Metadata {
160+
/// @Available(Swift, introduced: 6.2)
161+
/// }
111162
public static func signal(_ signal: CInt) -> Self {
112163
#if !SWT_NO_EXIT_TESTS
113164
Self(.signal(signal))
@@ -131,8 +182,8 @@ extension ExitTest.Condition: CustomStringConvertible {
131182
".failure"
132183
case .success:
133184
".success"
134-
case let .statusAtExit(statusAtExit):
135-
String(describing: statusAtExit)
185+
case let .exitStatus(exitStatus):
186+
String(describing: exitStatus)
136187
}
137188
#else
138189
fatalError("Unsupported")
@@ -149,19 +200,19 @@ extension ExitTest.Condition {
149200
/// Check whether or not an exit test condition matches a given exit status.
150201
///
151202
/// - Parameters:
152-
/// - statusAtExit: An exit status to compare against.
203+
/// - exitStatus: An exit status to compare against.
153204
///
154-
/// - Returns: Whether or not `self` and `statusAtExit` represent the same
155-
/// exit condition.
205+
/// - Returns: Whether or not `self` and `exitStatus` represent the same exit
206+
/// condition.
156207
///
157208
/// Two exit test conditions can be compared; if either instance is equal to
158209
/// ``failure``, it will compare equal to any instance except ``success``.
159-
func isApproximatelyEqual(to statusAtExit: StatusAtExit) -> Bool {
210+
func isApproximatelyEqual(to exitStatus: ExitStatus) -> Bool {
160211
// Strictly speaking, the C standard treats 0 as a successful exit code and
161212
// potentially distinct from EXIT_SUCCESS. To my knowledge, no modern
162213
// operating system defines EXIT_SUCCESS to any value other than 0, so the
163214
// distinction is academic.
164-
return switch (self._kind, statusAtExit) {
215+
return switch (self._kind, exitStatus) {
165216
case let (.success, .exitCode(exitCode)):
166217
exitCode == EXIT_SUCCESS
167218
case let (.failure, .exitCode(exitCode)):
@@ -170,7 +221,7 @@ extension ExitTest.Condition {
170221
// All terminating signals are considered failures.
171222
true
172223
default:
173-
self._kind == .statusAtExit(statusAtExit)
224+
self._kind == .exitStatus(exitStatus)
174225
}
175226
}
176227
}

0 commit comments

Comments
 (0)