-
Notifications
You must be signed in to change notification settings - Fork 102
/
Copy pathExitTest.Condition.swift
221 lines (206 loc) · 7.38 KB
/
ExitTest.Condition.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024–2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//
private import _TestingInternals
#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
extension ExitTest {
/// The possible conditions under which an exit test will complete.
///
/// Values of this type are used to describe the conditions under which an
/// exit test is expected to pass or fail by passing them to
/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or
/// ``require(exitsWith:observing:_:sourceLocation:performing:)``.
///
/// ## Topics
///
/// ### Successful exit conditions
///
/// - ``success``
///
/// ### Failing exit conditions
///
/// - ``failure``
/// - ``exitCode(_:)``
/// - ``signal(_:)``
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public struct Condition: Sendable {
/// An enumeration describing the possible conditions for an exit test.
private enum _Kind: Sendable, Equatable {
/// The exit test must exit with a particular exit status.
case exitStatus(ExitStatus)
/// The exit test must exit successfully.
case success
/// The exit test must exit with any failure.
case failure
}
/// The kind of condition.
private var _kind: _Kind
}
}
// MARK: -
#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
extension ExitTest.Condition {
/// A condition that matches when a process exits normally.
///
/// This condition matches the exit code `EXIT_SUCCESS`.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public static var success: Self {
Self(_kind: .success)
}
/// A condition that matches when a process exits abnormally
///
/// This condition matches any exit code other than `EXIT_SUCCESS` or any
/// signal that causes the process to exit.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public static var failure: Self {
Self(_kind: .failure)
}
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public init(_ exitStatus: ExitStatus) {
self.init(_kind: .exitStatus(exitStatus))
}
/// Creates a condition that matches when a process terminates with a given
/// exit code.
///
/// - Parameters:
/// - exitCode: The exit code reported by the process.
///
/// The C programming language defines two standard exit codes, `EXIT_SUCCESS`
/// and `EXIT_FAILURE`. Platforms may additionally define their own
/// non-standard exit codes:
///
/// | Platform | Header |
/// |-|-|
/// | 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) |
/// | 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) |
/// | FreeBSD | [`<stdlib.h>`](https://man.freebsd.org/cgi/man.cgi?exit(3)), [`<sysexits.h>`](https://man.freebsd.org/cgi/man.cgi?sysexits(3)) |
/// | OpenBSD | [`<stdlib.h>`](https://man.openbsd.org/exit.3), [`<sysexits.h>`](https://man.openbsd.org/sysexits.3) |
/// | Windows | [`<stdlib.h>`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure) |
///
/// @Comment {
/// See https://en.cppreference.com/w/c/program/EXIT_status for more
/// information about exit codes defined by the C standard.
/// }
///
/// On macOS, FreeBSD, OpenBSD, and Windows, the full exit code reported by
/// the process is reported to the parent process. Linux and other POSIX-like
/// systems may only reliably report the low unsigned 8 bits (0–255) of
/// the exit code.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public static func exitCode(_ exitCode: CInt) -> Self {
#if !SWT_NO_EXIT_TESTS
Self(.exitCode(exitCode))
#else
fatalError("Unsupported")
#endif
}
/// Creates a condition that matches when a process exits with a given signal.
///
/// - Parameters:
/// - signal: The signal that caused the process to exit.
///
/// The C programming language defines a number of standard signals. Platforms
/// may additionally define their own non-standard signal codes:
///
/// | Platform | Header |
/// |-|-|
/// | macOS | [`<signal.h>`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/signal.3.html) |
/// | Linux | [`<signal.h>`](https://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html) |
/// | FreeBSD | [`<signal.h>`](https://man.freebsd.org/cgi/man.cgi?signal(3)) |
/// | OpenBSD | [`<signal.h>`](https://man.openbsd.org/signal.3) |
/// | Windows | [`<signal.h>`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/signal-constants) |
///
/// @Comment {
/// See https://en.cppreference.com/w/c/program/SIG_types for more
/// information about signals defined by the C standard.
/// }
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public static func signal(_ signal: CInt) -> Self {
#if !SWT_NO_EXIT_TESTS
Self(.signal(signal))
#else
fatalError("Unsupported")
#endif
}
}
// MARK: - CustomStringConvertible
@_spi(Experimental)
#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
extension ExitTest.Condition: CustomStringConvertible {
public var description: String {
#if !SWT_NO_EXIT_TESTS
switch _kind {
case .failure:
".failure"
case .success:
".success"
case let .exitStatus(exitStatus):
String(describing: exitStatus)
}
#else
fatalError("Unsupported")
#endif
}
}
// MARK: - Comparison
#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
extension ExitTest.Condition {
/// Check whether or not an exit test condition matches a given exit status.
///
/// - Parameters:
/// - exitStatus: An exit status to compare against.
///
/// - Returns: Whether or not `self` and `exitStatus` represent the same exit
/// condition.
///
/// Two exit test conditions can be compared; if either instance is equal to
/// ``failure``, it will compare equal to any instance except ``success``.
func isApproximatelyEqual(to exitStatus: ExitStatus) -> Bool {
// Strictly speaking, the C standard treats 0 as a successful exit code and
// potentially distinct from EXIT_SUCCESS. To my knowledge, no modern
// operating system defines EXIT_SUCCESS to any value other than 0, so the
// distinction is academic.
return switch (self._kind, exitStatus) {
case let (.success, .exitCode(exitCode)):
exitCode == EXIT_SUCCESS
case let (.failure, .exitCode(exitCode)):
exitCode != EXIT_SUCCESS
case (.failure, .signal):
// All terminating signals are considered failures.
true
default:
self._kind == .exitStatus(exitStatus)
}
}
}