-
Notifications
You must be signed in to change notification settings - Fork 103
/
Copy pathExpectation+Macro.swift
586 lines (565 loc) · 24.8 KB
/
Expectation+Macro.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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 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
//
// MARK: Boolean expression checking
/// Check that an expectation has passed after a condition has been evaluated.
///
/// - Parameters:
/// - condition: The condition to be evaluated.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
///
/// If `condition` evaluates to `false`, an ``Issue`` is recorded for the test
/// that is running in the current task.
@freestanding(expression) public macro expect(
_ condition: Bool,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")
/// Check that an expectation has passed after a condition has been evaluated
/// and throw an error if it failed.
///
/// - Parameters:
/// - condition: The condition to be evaluated.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `condition` evaluates
/// to `false`.
///
/// If `condition` evaluates to `false`, an ``Issue`` is recorded for the test
/// that is running in the current task and an instance of
/// ``ExpectationFailedError`` is thrown.
@freestanding(expression) public macro require(
_ condition: Bool,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
// MARK: - Optional checking
/// Unwrap an optional value or, if it is `nil`, fail and throw an error.
///
/// - Parameters:
/// - optionalValue: The optional value to be unwrapped.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
///
/// - Returns: The unwrapped value of `optionalValue`.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `optionalValue` is
/// `nil`.
///
/// If `optionalValue` is `nil`, an ``Issue`` is recorded for the test that is
/// running in the current task and an instance of ``ExpectationFailedError`` is
/// thrown.
@freestanding(expression) public macro require<T>(
_ optionalValue: T?,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation
) -> T = #externalMacro(module: "TestingMacros", type: "RequireMacro")
/// Unwrap an optional boolean value or, if it is `nil`, fail and throw an
/// error.
///
/// - Parameters:
/// - optionalValue: The optional value to be unwrapped.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
///
/// - Returns: The unwrapped value of `optionalValue`.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `optionalValue` is
/// `nil`.
///
/// If `optionalValue` is `nil`, an ``Issue`` is recorded for the test that is
/// running in the current task and an instance of ``ExpectationFailedError`` is
/// thrown.
///
/// This overload of ``require(_:_:sourceLocation:)-6w9oo`` checks if
/// `optionalValue` may be ambiguous (i.e. it is unclear if the developer
/// intended to check for a boolean value or unwrap an optional boolean value)
/// and provides additional compile-time diagnostics when it is.
@freestanding(expression)
@_documentation(visibility: private)
public macro require(
_ optionalValue: Bool?,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation
) -> Bool = #externalMacro(module: "TestingMacros", type: "AmbiguousRequireMacro")
/// Unwrap an optional value or, if it is `nil`, fail and throw an error.
///
/// - Parameters:
/// - optionalValue: The optional value to be unwrapped.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
///
/// - Returns: The unwrapped value of `optionalValue`.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `optionalValue` is
/// `nil`.
///
/// If `optionalValue` is `nil`, an ``Issue`` is recorded for the test that is
/// running in the current task and an instance of ``ExpectationFailedError`` is
/// thrown.
///
/// This overload of ``require(_:_:sourceLocation:)-6w9oo`` is used when a
/// non-optional, non-`Bool` value is passed to `#require()`. It emits a warning
/// diagnostic indicating that the expectation is redundant.
@freestanding(expression)
@_documentation(visibility: private)
public macro require<T>(
_ optionalValue: T,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation
) -> T = #externalMacro(module: "TestingMacros", type: "NonOptionalRequireMacro")
// MARK: - Matching errors by type
/// Check that an expression always throws an error of a given type.
///
/// - Parameters:
/// - errorType: The type of error that is expected to be thrown. If
/// `expression` could throw _any_ error, or the specific type of thrown
/// error is unimportant, pass `(any Error).self`.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#expect()` when the expression `expression` _should_
/// throw an error of a given type:
///
/// ```swift
/// #expect(throws: EngineFailureError.self) {
/// FoodTruck.shared.engine.batteryLevel = 0
/// try FoodTruck.shared.engine.start()
/// }
/// ```
///
/// If `expression` does not throw an error, or if it throws an error that is
/// not an instance of `errorType`, an ``Issue`` is recorded for the test that
/// is running in the current task. Any value returned by `expression` is
/// discarded.
///
/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``expect(throws:_:sourceLocation:performing:)-1xr34`` instead. If
/// `expression` should _never_ throw any error, use
/// ``expect(throws:_:sourceLocation:performing:)-5lzjz`` instead.
@freestanding(expression) public macro expect<E, R>(
throws errorType: E.Type,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error
/// Check that an expression never throws an error.
///
/// - Parameters:
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#expect()` when the expression `expression` should
/// _never_ throw any error:
///
/// ```swift
/// #expect(throws: Never.self) {
/// FoodTruck.shared.engine.batteryLevel = 100
/// try FoodTruck.shared.engine.start()
/// }
/// ```
///
/// If `expression` throws an error, an ``Issue`` is recorded for the test that
/// is running in the current task. Any value returned by `expression` is
/// discarded.
///
/// Test functions can be annotated with `throws` and can throw errors which are
/// then recorded as issues when the test runs. If the intent is for a test to
/// fail when an error is thrown by `expression`, rather than to explicitly
/// check that an error is _not_ thrown by it, do not use this macro. Instead,
/// simply call the code in question and allow it to throw an error naturally.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``expect(throws:_:sourceLocation:performing:)-79piu`` instead. If the thrown
/// error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``expect(throws:_:sourceLocation:performing:)-1xr34`` instead.
@freestanding(expression) public macro expect<R>(
throws _: Never.Type,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")
/// Check that an expression always throws an error of a given type, and throw
/// an error if it does not.
///
/// - Parameters:
/// - errorType: The type of error that is expected to be thrown. If
/// `expression` could throw _any_ error, or the specific type of thrown
/// error is unimportant, pass `(any Error).self`.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `expression` does not
/// throw a matching error. The error thrown by `expression` is not rethrown.
///
/// Use this overload of `#require()` when the expression `expression` _should_
/// throw an error of a given type:
///
/// ```swift
/// try #require(throws: EngineFailureError.self) {
/// FoodTruck.shared.engine.batteryLevel = 0
/// try FoodTruck.shared.engine.start()
/// }
/// ```
///
/// If `expression` does not throw an error, or if it throws an error that is
/// not an instance of `errorType`, an ``Issue`` is recorded for the test that
/// is running in the current task and an instance of ``ExpectationFailedError``
/// is thrown. Any value returned by `expression` is discarded.
///
/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``require(throws:_:sourceLocation:performing:)-7v83e`` instead.
///
/// If `expression` should _never_ throw, simply invoke the code without using
/// this macro. The test will then fail if an error is thrown.
@freestanding(expression) public macro require<E, R>(
throws errorType: E.Type,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "RequireMacro") where E: Error
/// Check that an expression never throws an error, and throw an error if it
/// does.
///
/// - Parameters:
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `expression` throws
/// any error. The error thrown by `expression` is not rethrown.
@available(*, deprecated, message: "try #require(throws: Never.self) is redundant. Invoke non-throwing test code directly instead.")
@freestanding(expression) public macro require<R>(
throws _: Never.Type,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
// MARK: - Matching instances of equatable errors
/// Check that an expression always throws a specific error.
///
/// - Parameters:
/// - error: The error that is expected to be thrown.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#expect()` when the expression `expression` _should_
/// throw a specific error:
///
/// ```swift
/// #expect(throws: EngineFailureError.batteryDied) {
/// FoodTruck.shared.engine.batteryLevel = 0
/// try FoodTruck.shared.engine.start()
/// }
/// ```
///
/// If `expression` does not throw an error, or if it throws an error that is
/// not equal to `error`, an ``Issue`` is recorded for the test that is running
/// in the current task. Any value returned by `expression` is discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``expect(throws:_:sourceLocation:performing:)-79piu`` instead. If
/// `expression` should _never_ throw any error, use
/// ``expect(throws:_:sourceLocation:performing:)-5lzjz`` instead.
@freestanding(expression) public macro expect<E, R>(
throws error: E,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error & Equatable
/// Check that an expression always throws a specific error, and throw an error
/// if it does not.
///
/// - Parameters:
/// - error: The error that is expected to be thrown.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `expression` does not
/// throw a matching error. The error thrown by `expression` is not rethrown.
///
/// Use this overload of `#require()` when the expression `expression` _should_
/// throw a specific error:
///
/// ```swift
/// try #require(throws: EngineFailureError.batteryDied) {
/// FoodTruck.shared.engine.batteryLevel = 0
/// try FoodTruck.shared.engine.start()
/// }
/// ```
///
/// If `expression` does not throw an error, or if it throws an error that is
/// not equal to `error`, an ``Issue`` is recorded for the test that is running
/// in the current task and an instance of ``ExpectationFailedError`` is thrown.
/// Any value returned by `expression` is discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``require(throws:_:sourceLocation:performing:)-76bjn`` instead.
@freestanding(expression) public macro require<E, R>(
throws error: E,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "RequireMacro") where E: Error & Equatable
// MARK: - Arbitrary error matching
/// Check that an expression always throws an error matching some condition.
///
/// - Parameters:
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
/// - errorMatcher: A closure to invoke when `expression` throws an error that
/// indicates if it matched or not.
///
/// Use this overload of `#expect()` when the expression `expression` _should_
/// throw an error, but the logic to determine if the error matches is complex:
///
/// ```swift
/// #expect {
/// FoodTruck.shared.engine.batteryLevel = 0
/// try FoodTruck.shared.engine.start()
/// } throws: { error in
/// return error == EngineFailureError.batteryDied
/// || error == EngineFailureError.stillCharging
/// }
/// ```
///
/// If `expression` does not throw an error, if it throws an error that is
/// not matched by `errorMatcher`, or if `errorMatcher` throws an error
/// (including the error passed to it), an ``Issue`` is recorded for the test
/// that is running in the current task. Any value returned by `expression` is
/// discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``expect(throws:_:sourceLocation:performing:)-79piu`` instead. If the thrown
/// error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``expect(throws:_:sourceLocation:performing:)-1xr34`` instead. If an
/// error should _never_ be thrown, use
/// ``expect(throws:_:sourceLocation:performing:)-5lzjz`` instead.
@freestanding(expression) public macro expect<R>(
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R,
throws errorMatcher: (any Error) async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")
/// Check that an expression always throws an error matching some condition, and
/// throw an error if it does not.
///
/// - Parameters:
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
/// - errorMatcher: A closure to invoke when `expression` throws an error that
/// indicates if it matched or not.
///
/// - Throws: An instance of ``ExpectationFailedError`` if `expression` does not
/// throw a matching error. The error thrown by `expression` is not rethrown.
///
/// Use this overload of `#require()` when the expression `expression` _should_
/// throw an error, but the logic to determine if the error matches is complex:
///
/// ```swift
/// #expect {
/// FoodTruck.shared.engine.batteryLevel = 0
/// try FoodTruck.shared.engine.start()
/// } throws: { error in
/// return error == EngineFailureError.batteryDied
/// || error == EngineFailureError.stillCharging
/// }
/// ```
///
/// If `expression` does not throw an error, if it throws an error that is
/// not matched by `errorMatcher`, or if `errorMatcher` throws an error
/// (including the error passed to it), an ``Issue`` is recorded for the test
/// that is running in the current task and an instance of
/// ``ExpectationFailedError`` is thrown. Any value returned by `expression` is
/// discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``require(throws:_:sourceLocation:performing:)-76bjn`` instead. If the thrown error need
/// only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``require(throws:_:sourceLocation:performing:)-7v83e`` instead.
///
/// If `expression` should _never_ throw, simply invoke the code without using
/// this macro. The test will then fail if an error is thrown.
@freestanding(expression) public macro require<R>(
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: () async throws -> R,
throws errorMatcher: (any Error) async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
// MARK: - Exit tests
/// Check that an expression causes the process to terminate in a given fashion.
///
/// - Parameters:
/// - expectedExitCondition: The expected exit condition.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#expect()` when an expression will cause the current
/// process to terminate and the nature of that termination will determine if
/// the test passes or fails. For example, to test that calling `fatalError()`
/// causes a process to terminate:
///
/// ```swift
/// await #expect(exitsWith: .failure) {
/// fatalError()
/// }
/// ```
///
/// - Note: A call to this expectation macro is called an "exit test."
///
/// ## How exit tests are run
///
/// When an exit test is performed at runtime, the testing library starts a new
/// process with the same executable as the current process. The current task is
/// then suspended (as with `await`) and waits for the child process to
/// terminate. `expression` is not called in the parent process.
///
/// Meanwhile, in the child process, `expression` is called directly. To ensure
/// a clean environment for execution, it is not called within the context of
/// the original test. If `expression` does not terminate the child process, the
/// process is terminated automatically as if the main function of the child
/// process were allowed to return naturally. If an error is thrown from
/// `expression`, it is handed as if the error were thrown from `main()` and the
/// process is terminated.
///
/// Once the child process terminates, the parent process resumes and compares
/// its exit status against `exitCondition`. If they match, the exit test has
/// passed; otherwise, it has failed and an issue is recorded.
///
/// ## Runtime constraints
///
/// Exit tests cannot capture any state originating in the parent process or
/// from the enclosing lexical context. For example, the following exit test
/// will fail to compile because it captures an argument to the enclosing
/// parameterized test:
///
/// ```swift
/// @Test(arguments: 100 ..< 200)
/// func sellIceCreamCones(count: Int) async {
/// await #expect(exitsWith: .failure) {
/// precondition(
/// count < 10, // ERROR: A C function pointer cannot be formed from a
/// // closure that captures context
/// "Too many ice cream cones"
/// )
/// }
/// }
/// ```
///
/// An exit test cannot run within another exit test.
@_spi(Experimental)
#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
@freestanding(expression) public macro expect(
exitsWith expectedExitCondition: ExitCondition,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: @convention(thin) () async throws -> Void
) = #externalMacro(module: "TestingMacros", type: "ExitTestExpectMacro")
/// Check that an expression causes the process to terminate in a given fashion
/// and throw an error if it did not.
///
/// - Parameters:
/// - expectedExitCondition: The expected exit condition.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which recorded expectations and
/// issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// - Throws: An instance of ``ExpectationFailedError`` if the exit condition of
/// the child process does not equal `expectedExitCondition`.
///
/// Use this overload of `#require()` when an expression will cause the current
/// process to terminate and the nature of that termination will determine if
/// the test passes or fails. For example, to test that calling `fatalError()`
/// causes a process to terminate:
///
/// ```swift
/// try await #require(exitsWith: .failure) {
/// fatalError()
/// }
/// ```
///
/// - Note: A call to this expectation macro is called an "exit test."
///
/// ## How exit tests are run
///
/// When an exit test is performed at runtime, the testing library starts a new
/// process with the same executable as the current process. The current task is
/// then suspended (as with `await`) and waits for the child process to
/// terminate. `expression` is not called in the parent process.
///
/// Meanwhile, in the child process, `expression` is called directly. To ensure
/// a clean environment for execution, it is not called within the context of
/// the original test. If `expression` does not terminate the child process, the
/// process is terminated automatically as if the main function of the child
/// process were allowed to return naturally. If an error is thrown from
/// `expression`, it is handed as if the error were thrown from `main()` and the
/// process is terminated.
///
/// Once the child process terminates, the parent process resumes and compares
/// its exit status against `exitCondition`. If they match, the exit test has
/// passed; otherwise, it has failed and an issue is recorded.
///
/// ## Runtime constraints
///
/// Exit tests cannot capture any state originating in the parent process or
/// from the enclosing lexical context. For example, the following exit test
/// will fail to compile because it captures an argument to the enclosing
/// parameterized test:
///
/// ```swift
/// @Test(arguments: 100 ..< 200)
/// func sellIceCreamCones(count: Int) async throws {
/// try await #require(exitsWith: .failure) {
/// precondition(
/// count < 10, // ERROR: A C function pointer cannot be formed from a
/// // closure that captures context
/// "Too many ice cream cones"
/// )
/// }
/// }
/// ```
///
/// An exit test cannot run within another exit test.
@_spi(Experimental)
#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
@freestanding(expression) public macro require(
exitsWith expectedExitCondition: ExitCondition,
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: @convention(thin) () async throws -> Void
) = #externalMacro(module: "TestingMacros", type: "ExitTestRequireMacro")