@@ -17,22 +17,44 @@ private import _TestingInternals
1717
1818/// A type describing an exit test.
1919///
20- /// Instances of this type describe an exit test defined by the test author and
21- /// discovered or called at runtime.
22- @_spi ( Experimental) @_spi ( ForToolsIntegrationOnly)
20+ /// An instance of this type describes an exit test defined in a test target and
21+ /// discovered or called at runtime. You do not create instances of this type.
22+ ///
23+ /// You don't usually need to interact with an instance of this type. To create
24+ /// an exit test, use the ``expect(exitsWith:_:sourceLocation:performing:)``
25+ /// or ``require(exitsWith:_:sourceLocation:performing:)`` macro.
26+ @_spi ( Experimental)
2327public struct ExitTest : Sendable , ~ Copyable {
24- /// The expected exit condition of the exit test .
28+ /// This exit test's expected exit condition .
2529 public var expectedExitCondition : ExitCondition
2630
27- /// The body closure of the exit test.
28- fileprivate var body : @Sendable ( ) async throws -> Void = { }
29-
30- /// The source location of the exit test.
31+ /// The source location of this exit test.
3132 ///
3233 /// The source location is unique to each exit test and is consistent between
3334 /// processes, so it can be used to uniquely identify an exit test at runtime.
3435 public var sourceLocation : SourceLocation
3536
37+ /// The body closure of the exit test.
38+ ///
39+ /// Do not invoke this closure directly. Instead, invoke ``callAsFunction()``
40+ /// to run the exit test. Running the exit test will always terminate the
41+ /// current process.
42+ fileprivate var body : @Sendable ( ) async throws -> Void
43+
44+ /// Initialize an exit test at runtime.
45+ ///
46+ /// - Warning: This initializer is used to implement the `#expect(exitsWith:)`
47+ /// macro. Do not use it directly.
48+ public init (
49+ __expectedExitCondition expectedExitCondition: ExitCondition ,
50+ sourceLocation: SourceLocation ,
51+ body: @escaping @Sendable ( ) async throws -> Void = { }
52+ ) {
53+ self . expectedExitCondition = expectedExitCondition
54+ self . sourceLocation = sourceLocation
55+ self . body = body
56+ }
57+
3658 /// Disable crash reporting, crash logging, or core dumps for the current
3759 /// process.
3860 private static func _disableCrashReporting( ) {
@@ -83,6 +105,7 @@ public struct ExitTest: Sendable, ~Copyable {
83105 /// to terminate the process; if it does not, the testing library will
84106 /// terminate the process in a way that causes the corresponding expectation
85107 /// to fail.
108+ @_spi ( ForToolsIntegrationOnly)
86109 public consuming func callAsFunction( ) async -> Never {
87110 Self . _disableCrashReporting ( )
88111
@@ -102,44 +125,24 @@ public struct ExitTest: Sendable, ~Copyable {
102125
103126// MARK: - Discovery
104127
105- /// A protocol describing a type that contains an exit test.
106- ///
107- /// - Warning: This protocol is used to implement the `#expect(exitsWith:)`
108- /// macro. Do not use it directly.
109- @_alwaysEmitConformanceMetadata
110- @_spi ( Experimental)
111- public protocol __ExitTestContainer {
112- /// The expected exit condition of the exit test.
113- static var __expectedExitCondition : ExitCondition { get }
114-
115- /// The source location of the exit test.
116- static var __sourceLocation : SourceLocation { get }
117-
118- /// The body function of the exit test.
119- static var __body : @Sendable ( ) async throws -> Void { get }
120- }
121-
122128extension ExitTest {
123- /// A string that appears within all auto-generated types conforming to the
124- /// `__ExitTestContainer` protocol.
125- private static let _exitTestContainerTypeNameMagic = " __🟠$exit_test_body__ "
126-
127129 /// Find the exit test function at the given source location.
128130 ///
129131 /// - Parameters:
130132 /// - sourceLocation: The source location of the exit test to find.
131133 ///
132134 /// - Returns: The specified exit test function, or `nil` if no such exit test
133135 /// could be found.
136+ @_spi ( ForToolsIntegrationOnly)
134137 public static func find( at sourceLocation: SourceLocation ) -> Self ? {
135138 var result : Self ?
136139
137- enumerateTypes ( withNamesContaining : _exitTestContainerTypeNameMagic ) { _, type , stop in
138- if let type = type as? any __ExitTestContainer . Type , type . __sourceLocation == sourceLocation {
140+ enumerateTestContent ( ofKind : . exitTest , as : ExitTest . self ) { _, exitTest , _ , stop in
141+ if exitTest . sourceLocation == sourceLocation {
139142 result = ExitTest (
140- expectedExitCondition : type . __expectedExitCondition ,
141- body : type . __body ,
142- sourceLocation : type . __sourceLocation
143+ __expectedExitCondition : exitTest . expectedExitCondition ,
144+ sourceLocation : exitTest . sourceLocation ,
145+ body : exitTest . body
143146 )
144147 stop = true
145148 }
@@ -183,7 +186,7 @@ func callExitTest(
183186
184187 let actualExitCondition : ExitCondition
185188 do {
186- let exitTest = ExitTest ( expectedExitCondition : expectedExitCondition, sourceLocation: sourceLocation)
189+ let exitTest = ExitTest ( __expectedExitCondition : expectedExitCondition, sourceLocation: sourceLocation)
187190 actualExitCondition = try await configuration. exitTestHandler ( exitTest)
188191 } catch {
189192 // An error here would indicate a problem in the exit test handler such as a
@@ -295,7 +298,7 @@ extension ExitTest {
295298 // External tools authors should set up their own back channel mechanisms
296299 // and ensure they're installed before calling ExitTest.callAsFunction().
297300 guard var result = find ( at: sourceLocation) else {
298- return nil
301+ fatalError ( " Could not find an exit test that should have been located at \( sourceLocation ) . " )
299302 }
300303
301304 // We can't say guard let here because it counts as a consume.
0 commit comments