13
13
/// This protocol is used to abstract away the common implementation of test and
14
14
/// test case cancellation.
15
15
protocol TestCancellable : Sendable {
16
- /// Cancel the current instance of this type.
17
- ///
18
- /// - Parameters:
19
- /// - skipInfo: Information about the cancellation event.
20
- ///
21
- /// - Throws: An error indicating that the current instance of this type has
22
- /// been cancelled.
23
- ///
24
- /// Note that the public ``Test/cancel(_:sourceLocation:)`` function has a
25
- /// different signature and accepts a source location rather than a source
26
- /// context value.
27
- static func cancel( with skipInfo: SkipInfo ) throws -> Never
28
-
29
16
/// Make an instance of ``Event/Kind`` appropriate for an instance of this
30
17
/// type.
31
18
///
@@ -108,9 +95,8 @@ extension TestCancellable {
108
95
} onCancel: {
109
96
// The current task was cancelled, so cancel the test case or test
110
97
// associated with it.
111
-
112
98
let skipInfo = _currentSkipInfo ?? SkipInfo ( sourceContext: SourceContext ( backtrace: . current( ) , sourceLocation: nil ) )
113
- _ = try ? Self . cancel ( with: skipInfo)
99
+ _ = try ? Test . cancel ( with: skipInfo)
114
100
}
115
101
}
116
102
}
@@ -125,9 +111,7 @@ extension TestCancellable {
125
111
/// is set and we need fallback handling.
126
112
/// - testAndTestCase: The test and test case to use when posting an event.
127
113
/// - skipInfo: Information about the cancellation event.
128
- ///
129
- /// - Throws: An instance of ``SkipInfo`` describing the cancellation.
130
- private func _cancel< T> ( _ cancellableValue: T ? , for testAndTestCase: ( Test ? , Test . Case ? ) , skipInfo: SkipInfo ) throws -> Never where T: TestCancellable {
114
+ private func _cancel< T> ( _ cancellableValue: T ? , for testAndTestCase: ( Test ? , Test . Case ? ) , skipInfo: SkipInfo ) where T: TestCancellable {
131
115
if cancellableValue != nil {
132
116
// If the current test case is still running, take its task property (which
133
117
// signals to subsequent callers that it has been cancelled.)
@@ -171,26 +155,25 @@ private func _cancel<T>(_ cancellableValue: T?, for testAndTestCase: (Test?, Tes
171
155
issue. record ( )
172
156
}
173
157
}
174
-
175
- throw skipInfo
176
158
}
177
159
178
160
// MARK: - Test cancellation
179
161
180
162
extension Test : TestCancellable {
181
- /// Cancel the current test.
163
+ /// Cancel the current test or test case .
182
164
///
183
165
/// - Parameters:
184
- /// - comment: A comment describing why you are cancelling the test.
166
+ /// - comment: A comment describing why you are cancelling the test or test
167
+ /// case.
185
168
/// - sourceLocation: The source location to which the testing library will
186
169
/// attribute the cancellation.
187
170
///
188
- /// - Throws: An error indicating that the current test case has been
171
+ /// - Throws: An error indicating that the current test or test case has been
189
172
/// cancelled.
190
173
///
191
- /// The testing library runs each test in its own task. When you call this
192
- /// function, the testing library cancels the task associated with the current
193
- /// test:
174
+ /// The testing library runs each test and each test case in its own task.
175
+ /// When you call this function, the testing library cancels the task
176
+ /// associated with the current test:
194
177
///
195
178
/// ```swift
196
179
/// @Test func `Food truck is well-stocked`() throws {
@@ -201,11 +184,17 @@ extension Test: TestCancellable {
201
184
/// }
202
185
/// ```
203
186
///
204
- /// If the current test is parameterized, all of its pending and running test
205
- /// cases are cancelled. If the current test is a suite, all of its pending
206
- /// and running tests are cancelled. If you have already cancelled the current
207
- /// test or if it has already finished running, this function throws an error
208
- /// but does not attempt to cancel the test a second time.
187
+ /// If the current test is a parameterized test function, this function
188
+ /// instead cancels the current test case. Other test cases in the test
189
+ /// function are not affected.
190
+ ///
191
+ /// If the current test is a suite, the testing library cancels all of its
192
+ /// pending and running tests.
193
+ ///
194
+ /// If you have already cancelled the current test or if it has already
195
+ /// finished running, this function throws an error to indicate that the
196
+ /// current test has been cancelled, but does not attempt to cancel the test a
197
+ /// second time.
209
198
///
210
199
/// @Comment {
211
200
/// TODO: Document the interaction between an exit test and test
@@ -217,89 +206,53 @@ extension Test: TestCancellable {
217
206
/// - Important: If the current task is not associated with a test (for
218
207
/// example, because it was created with [`Task.detached(name:priority:operation:)`](https://developer.apple.com/documentation/swift/task/detached(name:priority:operation:)-795w1))
219
208
/// this function records an issue and cancels the current task.
220
- ///
221
- /// To cancel the current test case but leave other test cases of the current
222
- /// test alone, call ``Test/Case/cancel(_:sourceLocation:)`` instead.
223
209
@_spi ( Experimental)
224
210
public static func cancel( _ comment: Comment ? = nil , sourceLocation: SourceLocation = #_sourceLocation) throws -> Never {
225
211
let skipInfo = SkipInfo ( comment: comment, sourceContext: SourceContext ( backtrace: nil , sourceLocation: sourceLocation) )
226
212
try Self . cancel ( with: skipInfo)
227
213
}
228
214
229
- static func cancel( with skipInfo: SkipInfo ) throws -> Never {
230
- let test = Test . current
231
- try _cancel ( test, for: ( test, nil ) , skipInfo: skipInfo)
232
- }
233
-
234
- static func makeCancelledEventKind( with skipInfo: SkipInfo ) -> Event . Kind {
235
- . testCancelled( skipInfo)
236
- }
237
- }
238
-
239
- // MARK: - Test case cancellation
240
-
241
- extension Test . Case : TestCancellable {
242
- /// Cancel the current test case.
215
+ /// Cancel the current test or test case.
243
216
///
244
217
/// - Parameters:
245
- /// - comment: A comment describing why you are cancelling the test case.
246
- /// - sourceLocation: The source location to which the testing library will
247
- /// attribute the cancellation.
218
+ /// - skipInfo: Information about the cancellation event.
248
219
///
249
- /// - Throws: An error indicating that the current test case has been
220
+ /// - Throws: An error indicating that the current test or test case has been
250
221
/// cancelled.
251
222
///
252
- /// The testing library runs each test case of a test in its own task. When
253
- /// you call this function, the testing library cancels the task associated
254
- /// with the current test case:
255
- ///
256
- /// ```swift
257
- /// @Test(arguments: [Food.burger, .fries, .iceCream])
258
- /// func `Food truck is well-stocked`(_ food: Food) throws {
259
- /// if food == .iceCream && Season.current == .winter {
260
- /// try Test.Case.cancel("It's too cold for ice cream.")
261
- /// }
262
- /// // ...
263
- /// }
264
- /// ```
265
- ///
266
- /// If the current test is parameterized, the test's other test cases continue
267
- /// running. If the current test case has already been cancelled, this
268
- /// function throws an error but does not attempt to cancel the test case a
269
- /// second time.
270
- ///
271
- /// @Comment {
272
- /// TODO: Document the interaction between an exit test and test
273
- /// cancellation. In particular, the error thrown by this function isn't
274
- /// thrown into the parent process and task cancellation doesn't propagate
275
- /// (because the exit test _de facto_ runs in a detached task.)
276
- /// }
277
- ///
278
- /// - Important: If the current task is not associated with a test case (for
279
- /// example, because it was created with [`Task.detached(name:priority:operation:)`](https://developer.apple.com/documentation/swift/task/detached(name:priority:operation:)-795w1))
280
- /// this function records an issue and cancels the current task.
281
- ///
282
- /// To cancel all test cases in the current test, call
283
- /// ``Test/cancel(_:sourceLocation:)`` instead.
284
- @_spi ( Experimental)
285
- public static func cancel( _ comment: Comment ? = nil , sourceLocation: SourceLocation = #_sourceLocation) throws -> Never {
286
- let skipInfo = SkipInfo ( comment: comment, sourceContext: SourceContext ( backtrace: nil , sourceLocation: sourceLocation) )
287
- try Self . cancel ( with: skipInfo)
288
- }
289
-
223
+ /// Note that the public ``Test/cancel(_:sourceLocation:)`` function has a
224
+ /// different signature and accepts a source location rather than an instance
225
+ /// of ``SkipInfo``.
290
226
static func cancel( with skipInfo: SkipInfo ) throws -> Never {
291
227
let test = Test . current
292
228
let testCase = Test . Case. current
293
229
294
- do {
295
- // Cancel the current test case (if it's nil, that's the API misuse path.)
296
- try _cancel ( testCase, for: ( test, testCase) , skipInfo: skipInfo)
297
- } catch _ where test? . isParameterized == false {
298
- // The current test is not parameterized, so cancel the whole test too.
299
- try _cancel ( test, for: ( test, nil ) , skipInfo: skipInfo)
230
+ if let testCase {
231
+ // Cancel the current test case.
232
+ _cancel ( testCase, for: ( test, testCase) , skipInfo: skipInfo)
300
233
}
234
+
235
+ if let test {
236
+ if !test. isParameterized {
237
+ // The current test is not parameterized, so cancel the whole test too.
238
+ _cancel ( test, for: ( test, nil ) , skipInfo: skipInfo)
239
+ }
240
+ } else {
241
+ // There is no current test (this is the API misuse path.)
242
+ _cancel ( test, for: ( test, nil ) , skipInfo: skipInfo)
243
+ }
244
+
245
+ throw skipInfo
301
246
}
302
247
248
+ static func makeCancelledEventKind( with skipInfo: SkipInfo ) -> Event . Kind {
249
+ . testCancelled( skipInfo)
250
+ }
251
+ }
252
+
253
+ // MARK: - Test case cancellation
254
+
255
+ extension Test . Case : TestCancellable {
303
256
static func makeCancelledEventKind( with skipInfo: SkipInfo ) -> Event . Kind {
304
257
. testCaseCancelled( skipInfo)
305
258
}
0 commit comments