Skip to content

Commit 22bfdc2

Browse files
committed
Add alternative considered: parameter packs
1 parent 041c19e commit 22bfdc2

File tree

1 file changed

+42
-6
lines changed

1 file changed

+42
-6
lines changed

proposals/testing/NNNN-exit-tests.md

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ describes how the child process is expected to have exited:
246246
standard, but not to the degree that signals are supported by POSIX-like or
247247
UNIX-derived operating systems. Swift Testing makes a "best effort" to emulate
248248
signal-handling support on Windows. See [this](https://forums.swift.org/t/swift-on-windows-question-about-signals-and-exceptions/76640/2)
249-
Swift forum message for more information.
249+
Swift forum message for more information.
250250

251251
The type is declared as:
252252

@@ -287,7 +287,7 @@ extension ExitTest {
287287
public static var failure: Self { get }
288288

289289
public init(_ statusAtExit: StatusAtExit)
290-
290+
291291
/// Creates a condition that matches when a process terminates with a given
292292
/// exit code.
293293
///
@@ -311,7 +311,7 @@ extension ExitTest {
311311
/// systems may only reliably report the low unsigned 8 bits (0–255) of
312312
/// the exit code.
313313
public static func exitCode(_ exitCode: CInt) -> Self
314-
314+
315315
/// Creates a condition that matches when a process terminates with a given
316316
/// signal.
317317
///
@@ -624,7 +624,7 @@ and control returns to the test function that is awaiting the exit test:
624624

625625
```swift
626626
await #expect(exitsWith: .failure) {
627-
throw TacoError.noTacosFound
627+
throw TacoError.noTacosFound
628628
}
629629
```
630630

@@ -737,7 +737,7 @@ let result = try await #require(process, exitsWith: .success)
737737
case signal(CInt)
738738
}
739739
```
740-
740+
741741
This simplified the set of types used for exit tests, but made comparing two
742742
exit conditions complicated and necessitated a `==` operator that did not
743743
satisfy the requirements of the `Equatable` protocol.
@@ -756,7 +756,7 @@ let result = try await #require(process, exitsWith: .success)
756756
I settled on `StatusAtExit` because it was distinct and makes it clear that it
757757
represents the status of a process _at exit time_. `ExitStatus` could be
758758
interpreted as the status of the exit itself, i.e.:
759-
759+
760760
```swift
761761
enum ExitStatus {
762762
case running
@@ -766,6 +766,42 @@ let result = try await #require(process, exitsWith: .success)
766766
}
767767
```
768768

769+
- Using parameter packs to specify observed values and return types:
770+
771+
```swift
772+
@freestanding(expression) public macro require<each T>(
773+
exitsWith expectedExitCondition: ExitTest.Condition,
774+
observing observedValues: (repeat (KeyPath<ExitTest.Result, each T>)) = (),
775+
_ comment: @autoclosure () -> Comment? = nil,
776+
sourceLocation: SourceLocation = #_sourceLocation,
777+
performing expression: @escaping @Sendable @convention(thin) () async throws -> Void
778+
) -> (repeat each T)
779+
```
780+
781+
Using a parameter pack in this way would make it impossible to access
782+
properties of the returned `ExitTest.Result` value that weren't observed, and
783+
in general would mean developers wouldn't even need to use `ExitTest.Result`:
784+
785+
```swift
786+
let (status, stderr) = try await #expect(
787+
exitsWith: .failure,
788+
observing: (\.statusAtExit, \.standardErrorContent)
789+
) { ... }
790+
#expect(status == ...)
791+
#expect(stderr.contains(...))
792+
```
793+
794+
Unfortunately, the `#expect(exitsWith:)` and `#require(exitsWith:)` macros do
795+
not have enough information at compile time to correctly infer the types of
796+
the key paths passed as `observedValues` above, so we end up with rather
797+
obscure errors:
798+
799+
> 🛑 Cannot convert value of type 'KeyPath<_, _>' to expected argument type
800+
> 'KeyPath<ExitTest.Result, _>'
801+
802+
If, in the future, this error is resolved, we may wish to revisit this option,
803+
so it can also be considered a "future direction" for the feature.
804+
769805
- Changing the implementation of `precondition()`, `fatalError()`, etc. in the
770806
standard library so that they do not terminate the current process while
771807
testing, thus removing the need to spawn a child process for an exit test.

0 commit comments

Comments
 (0)