Skip to content

Commit 1250b3c

Browse files
committed
Move backchannel setup to the entry point side of things since it only applies to our own entry point and third parties would need to supply their own configuration/etc. anyway
1 parent 5d5a4a1 commit 1250b3c

File tree

1 file changed

+64
-48
lines changed

1 file changed

+64
-48
lines changed

Sources/Testing/ExitTests/ExitTest.swift

+64-48
Original file line numberDiff line numberDiff line change
@@ -66,33 +66,6 @@ public struct ExitTest: Sendable {
6666
#endif
6767
}
6868

69-
/// The back channel file handle set up by the parent process.
70-
///
71-
/// The value of this property is a file handle open for writing to which
72-
/// events should be written, or `nil` if the file handle could not be
73-
/// resolved.
74-
private static let _backChannel: FileHandle? = {
75-
guard let backChannelEnvironmentVariable = Environment.variable(named: "SWT_EXPERIMENTAL_BACKCHANNEL") else {
76-
return nil
77-
}
78-
79-
var fd: CInt?
80-
#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD)
81-
fd = CInt(backChannelEnvironmentVariable)
82-
#elseif os(Windows)
83-
if let handle = UInt(backChannelEnvironmentVariable).flatMap(HANDLE.init(bitPattern:)) {
84-
fd = _open_osfhandle(Int(bitPattern: handle), _O_WRONLY | _O_BINARY)
85-
}
86-
#else
87-
#warning("Platform-specific implementation missing: back-channel pipe unavailable")
88-
#endif
89-
guard let fd, fd >= 0 else {
90-
return nil
91-
}
92-
93-
return try? FileHandle(unsafePOSIXFileDescriptor: fd, mode: "wb")
94-
}()
95-
9669
/// Call the exit test in the current process.
9770
///
9871
/// This function invokes the closure originally passed to
@@ -103,26 +76,8 @@ public struct ExitTest: Sendable {
10376
public func callAsFunction() async -> Never {
10477
Self._disableCrashReporting()
10578

106-
// Set up the configuration for this process.
107-
var configuration = Configuration()
108-
109-
// Encode events as JSON and write them to the back channel file handle.
110-
var eventHandler = ABIv0.Record.eventHandler(encodeAsJSONLines: true) { json in
111-
try? Self._backChannel?.write(json)
112-
}
113-
114-
// Only forward issue-recorded events. (If we start handling other kinds
115-
// of event in the future, we can forward them too.)
116-
eventHandler = { [eventHandler] event, eventContext in
117-
if case .issueRecorded = event.kind {
118-
eventHandler(event, eventContext)
119-
}
120-
}
121-
122-
configuration.eventHandler = eventHandler
123-
12479
do {
125-
try await Configuration.withCurrent(configuration, perform: body)
80+
try await body()
12681
} catch {
12782
_errorInMain(error)
12883
}
@@ -276,6 +231,33 @@ extension ExitTest {
276231
/// recording any issues that occur.
277232
public typealias Handler = @Sendable (_ exitTest: borrowing ExitTest) async throws -> ExitCondition
278233

234+
/// The back channel file handle set up by the parent process.
235+
///
236+
/// The value of this property is a file handle open for writing to which
237+
/// events should be written, or `nil` if the file handle could not be
238+
/// resolved.
239+
private static let _backChannelForEntryPoint: FileHandle? = {
240+
guard let backChannelEnvironmentVariable = Environment.variable(named: "SWT_EXPERIMENTAL_BACKCHANNEL") else {
241+
return nil
242+
}
243+
244+
var fd: CInt?
245+
#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD)
246+
fd = CInt(backChannelEnvironmentVariable)
247+
#elseif os(Windows)
248+
if let handle = UInt(backChannelEnvironmentVariable).flatMap(HANDLE.init(bitPattern:)) {
249+
fd = _open_osfhandle(Int(bitPattern: handle), _O_WRONLY | _O_BINARY)
250+
}
251+
#else
252+
#warning("Platform-specific implementation missing: back-channel pipe unavailable")
253+
#endif
254+
guard let fd, fd >= 0 else {
255+
return nil
256+
}
257+
258+
return try? FileHandle(unsafePOSIXFileDescriptor: fd, mode: "wb")
259+
}()
260+
279261
/// Find the exit test function specified in the environment of the current
280262
/// process, if any.
281263
///
@@ -286,14 +268,48 @@ extension ExitTest {
286268
/// `__swiftPMEntryPoint()` function. The effect of using it under other
287269
/// configurations is undefined.
288270
static func findInEnvironmentForEntryPoint() -> Self? {
271+
var result: Self?
272+
289273
if var sourceLocationString = Environment.variable(named: "SWT_EXPERIMENTAL_EXIT_TEST_SOURCE_LOCATION") {
290-
return try? sourceLocationString.withUTF8 { sourceLocationBuffer in
274+
result = try? sourceLocationString.withUTF8 { sourceLocationBuffer in
291275
let sourceLocationBuffer = UnsafeRawBufferPointer(sourceLocationBuffer)
292276
let sourceLocation = try JSON.decode(SourceLocation.self, from: sourceLocationBuffer)
293277
return find(at: sourceLocation)
294278
}
295279
}
296-
return nil
280+
281+
// If an exit test was found, inject back channel handling into its body.
282+
// External tools authors should set up their own back channel mechanisms
283+
// and ensure they're installed before calling ExitTest.callAsFunction().
284+
result = result.map { result in
285+
// We can't say guard let here because it counts as a consume.
286+
guard _backChannelForEntryPoint != nil else {
287+
return result
288+
}
289+
290+
// Set up the configuration for this process.
291+
var configuration = Configuration()
292+
293+
// Encode events as JSON and write them to the back channel file handle.
294+
// Only forward issue-recorded events. (If we start handling other kinds
295+
// of event in the future, we can forward them too.)
296+
let eventHandler = ABIv0.Record.eventHandler(encodeAsJSONLines: true) { json in
297+
try? _backChannelForEntryPoint?.write(json)
298+
}
299+
configuration.eventHandler = { event, eventContext in
300+
if case .issueRecorded = event.kind {
301+
eventHandler(event, eventContext)
302+
}
303+
}
304+
305+
var result = result
306+
result.body = { [configuration, body = result.body] in
307+
try await Configuration.withCurrent(configuration, perform: body)
308+
}
309+
return result
310+
}
311+
312+
return result
297313
}
298314

299315
/// The exit test handler used when integrating with Swift Package Manager via

0 commit comments

Comments
 (0)