Skip to content

Commit 9d8d27d

Browse files
committed
Get it building on 6.2
1 parent af4579b commit 9d8d27d

File tree

4 files changed

+199
-90
lines changed

4 files changed

+199
-90
lines changed

Sources/Testing/ABI/EntryPoints/EntryPoint.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ func entryPoint(passing args: __CommandLineArguments_v0?, eventHandler: Event.Ha
3737
#endif
3838

3939
let args = try args ?? parseCommandLineArguments(from: CommandLine.arguments)
40+
41+
if let library = args.testingLibrary.flatMap(Library.init(named:)) {
42+
return await library.callEntryPoint(passing: args)
43+
}
44+
4045
// Configure the test runner.
4146
var configuration = try configurationForEntryPoint(from: args)
4247

Sources/Testing/ABI/EntryPoints/Library.swift

Lines changed: 114 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,28 @@
1111
@_spi(Experimental) @_spi(ForToolsIntegrationOnly) private import _TestDiscovery
1212
private import _TestingInternals
1313

14-
@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
14+
/// A type representing a testing library such as Swift Testing or XCTest.
15+
@_spi(Experimental)
1516
public struct Library: Sendable {
16-
/* @c */ fileprivate struct Record {
17-
typealias EntryPoint = @Sendable @convention(c) (
18-
_ configurationJSON: UnsafeRawPointer,
19-
_ configurationJSONByteCount: Int,
20-
_ reserved: UInt,
21-
_ context: UnsafeRawPointer,
22-
_ recordJSONHandler: RecordJSONHandler,
23-
_ completionHandler: CompletionHandler
24-
) -> Void
25-
26-
typealias RecordJSONHandler = @Sendable @convention(c) (
27-
_ recordJSON: UnsafeRawPointer,
28-
_ recordJSONByteCount: Int,
29-
_ reserved: UInt,
30-
_ context: UnsafeRawPointer
31-
) -> Void
32-
33-
typealias CompletionHandler = @Sendable @convention(c) (
34-
_ exitCode: CInt,
35-
_ reserved: UInt,
36-
_ context: UnsafeRawPointer
37-
) -> Void
38-
39-
nonisolated(unsafe) var name: UnsafePointer<CChar>
40-
var entryPoint: EntryPoint
41-
var reserved: UInt
42-
}
17+
/// - Important: The in-memory layout of ``Library`` must _exactly_ match the
18+
/// layout of this type. As such, it must not contain any other stored
19+
/// properties.
20+
private nonisolated(unsafe) var _library: SWTLibrary
4321

44-
private var _record: Record
22+
fileprivate init(_ library: SWTLibrary) {
23+
_library = library
24+
}
4525

26+
/// The human-readable name of this library.
27+
///
28+
/// For example, the value of this property for an instance of this type that
29+
/// represents the Swift Testing library is `"Swift Testing"`.
4630
public var name: String {
47-
String(validatingCString: _record.name) ?? ""
31+
String(validatingCString: _library.name) ?? ""
4832
}
4933

34+
/// Call the entry point function of this library.
35+
@_spi(ForToolsIntegrationOnly)
5036
public func callEntryPoint(
5137
passing args: __CommandLineArguments_v0? = nil,
5238
recordHandler: @escaping @Sendable (
@@ -61,7 +47,7 @@ public struct Library: Sendable {
6147
}
6248
} catch {
6349
// TODO: more advanced error recovery?
64-
return EINVAL
50+
return EXIT_FAILURE
6551
}
6652

6753
return await withCheckedContinuation { continuation in
@@ -76,20 +62,20 @@ public struct Library: Sendable {
7662
) as AnyObject
7763
).toOpaque()
7864
configurationJSON.withUnsafeBytes { configurationJSON in
79-
_record.entryPoint(
65+
_library.entryPoint(
8066
configurationJSON.baseAddress!,
8167
configurationJSON.count,
8268
0,
8369
context,
8470
/* recordJSONHandler: */ { recordJSON, recordJSONByteCount, _, context in
85-
guard let context = Unmanaged<AnyObject>.fromOpaque(context).takeUnretainedValue() as? Context else {
71+
guard let context = Unmanaged<AnyObject>.fromOpaque(context!).takeUnretainedValue() as? Context else {
8672
return
8773
}
8874
let recordJSON = UnsafeRawBufferPointer(start: recordJSON, count: recordJSONByteCount)
8975
context.recordHandler(recordJSON)
9076
},
9177
/* completionHandler: */ { exitCode, _, context in
92-
guard let context = Unmanaged<AnyObject>.fromOpaque(context).takeRetainedValue() as? Context else {
78+
guard let context = Unmanaged<AnyObject>.fromOpaque(context!).takeRetainedValue() as? Context else {
9379
return
9480
}
9581
context.continuation.resume(returning: exitCode)
@@ -102,22 +88,33 @@ public struct Library: Sendable {
10288

10389
// MARK: - Discovery
10490

105-
@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
106-
extension Library.Record: DiscoverableAsTestContent {
107-
static var testContentKind: TestContentKind {
91+
/// A helper protocol that prevents the conformance of ``Library`` to
92+
/// ``DiscoverableAsTestContent`` from being emitted into the testing library's
93+
/// Swift module or interface files.
94+
private protocol _DiscoverableAsTestContent: DiscoverableAsTestContent {}
95+
96+
extension Library: _DiscoverableAsTestContent {
97+
fileprivate static var testContentKind: TestContentKind {
10898
"main"
10999
}
110100

111-
typealias TestContentAccessorHint = UnsafePointer<CChar>
101+
fileprivate typealias TestContentAccessorHint = UnsafePointer<CChar>
112102
}
113103

114-
@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
104+
@_spi(Experimental)
115105
extension Library {
106+
private static let _validateMemoryLayout: Void = {
107+
assert(MemoryLayout<Library>.size == MemoryLayout<SWTLibrary>.size, "Library.size (\(MemoryLayout<Library>.size)) != SWTLibrary.size (\(MemoryLayout<SWTLibrary>.size)). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
108+
assert(MemoryLayout<Library>.stride == MemoryLayout<SWTLibrary>.stride, "Library.stride (\(MemoryLayout<Library>.stride)) != SWTLibrary.stride (\(MemoryLayout<SWTLibrary>.stride)). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
109+
assert(MemoryLayout<Library>.alignment == MemoryLayout<SWTLibrary>.alignment, "Library.alignment (\(MemoryLayout<Library>.alignment)) != SWTLibrary.alignment (\(MemoryLayout<SWTLibrary>.alignment)). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
110+
}()
111+
112+
@_spi(ForToolsIntegrationOnly)
116113
public init?(named name: String) {
114+
Self._validateMemoryLayout
117115
let result = name.withCString { name in
118-
Record.allTestContentRecords().lazy
116+
Library.allTestContentRecords().lazy
119117
.compactMap { $0.load(withHint: name) }
120-
.map(Self.init(_record:))
121118
.first
122119
}
123120
if let result {
@@ -127,60 +124,83 @@ extension Library {
127124
}
128125
}
129126

127+
@_spi(ForToolsIntegrationOnly)
130128
public static var all: some Sequence<Self> {
131-
Record.allTestContentRecords().lazy
132-
.compactMap { $0.load() }
133-
.map(Self.init(_record:))
129+
Self._validateMemoryLayout
130+
return Library.allTestContentRecords().lazy.compactMap { $0.load() }
134131
}
135132
}
136133

137134
// MARK: - Our very own entry point
138135

139-
private let testingLibraryDiscoverableEntryPoint: Library.Record.EntryPoint = { configurationJSON, configurationJSONByteCount, _, context, recordJSONHandler, completionHandler in
136+
private let _discoverableEntryPoint: SWTLibraryEntryPoint = { configurationJSON, configurationJSONByteCount, _, context, recordJSONHandler, completionHandler in
137+
// Capture appropriate state from the arguments to forward into the canonical
138+
// entry point function.
139+
let contextBitPattern = UInt(bitPattern: context)
140+
let configurationJSON = UnsafeRawBufferPointer(start: configurationJSON, count: configurationJSONByteCount)
141+
var args: __CommandLineArguments_v0
142+
let eventHandler: Event.Handler
140143
do {
141-
nonisolated(unsafe) let context = context
142-
let configurationJSON = UnsafeRawBufferPointer(start: configurationJSON, count: configurationJSONByteCount)
143-
let args = try JSON.decode(__CommandLineArguments_v0.self, from: configurationJSON)
144-
let eventHandler = try eventHandlerForStreamingEvents(withVersionNumber: args.eventStreamVersionNumber, encodeAsJSONLines: false) { recordJSON in
145-
recordJSONHandler(recordJSON.baseAddress!, recordJSON.count, 0, context)
146-
}
147-
148-
Task.detached {
149-
let exitCode = await Testing.entryPoint(passing: args, eventHandler: eventHandler)
150-
completionHandler(exitCode, 0, context)
144+
args = try JSON.decode(__CommandLineArguments_v0.self, from: configurationJSON)
145+
eventHandler = try eventHandlerForStreamingEvents(withVersionNumber: args.eventStreamVersionNumber, encodeAsJSONLines: false) { recordJSON in
146+
let context = UnsafeRawPointer(bitPattern: contextBitPattern)!
147+
recordJSONHandler(recordJSON.baseAddress!, recordJSON.count, 0, context)
151148
}
152149
} catch {
153150
// TODO: more advanced error recovery?
154-
return completionHandler(EINVAL, 0, context)
151+
return completionHandler(EXIT_FAILURE, 0, context)
155152
}
153+
154+
// Avoid infinite recursion. (Other libraries don't need to clear this field.)
155+
args.testingLibrary = nil
156+
157+
#if !SWT_NO_UNSTRUCTURED_TASKS
158+
Task.detached { [args] in
159+
let context = UnsafeRawPointer(bitPattern: contextBitPattern)!
160+
let exitCode = await Testing.entryPoint(passing: args, eventHandler: eventHandler)
161+
completionHandler(exitCode, 0, context)
162+
}
163+
#else
164+
let exitCode = Task.runInline { [args] in
165+
await Testing.entryPoint(passing: args, eventHandler: eventHandler)
166+
}
167+
completionHandler(exitCode, 0, context)
168+
#endif
156169
}
157170

158-
private func testingLibraryDiscoverableAccessor(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _ reserved: UInt) -> CBool {
171+
private func _discoverableAccessor(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _ reserved: UInt) -> CBool {
172+
#if !hasFeature(Embedded)
173+
// Make sure that the caller supplied the right Swift type. If a testing
174+
// library is implemented in a language other than Swift, they can either:
175+
// ignore this argument; or ask the Swift runtime for the type metadata
176+
// pointer and compare it against the value `type.pointee` (`*type` in C).
177+
guard type.load(as: Any.Type.self) == Library.self else {
159178
return false
160-
// #if !hasFeature(Embedded)
161-
// guard type.load(as: Any.Type.self) == Library.Record.self else {
162-
// return false
163-
// }
164-
// #endif
165-
// let hint = hint.map { $0.load(as: UnsafePointer<CChar>.self) }
166-
// if let hint {
167-
// guard let hint = String(validatingCString: hint),
168-
// String(hint.filter(\.isLetter)).lowercased() == "swifttesting" else {
169-
// return false
170-
// }
171-
// }
172-
// let name: StaticString = "Swift Testing"
173-
// name.utf8Start.withMemoryRebound(to: CChar.self, capacity: name.utf8CodeUnitCount + 1) { name in
174-
// _ = outValue.initializeMemory(
175-
// as: Library.Record.self,
176-
// to: .init(
177-
// name: name,
178-
// entryPoint: testingLibraryDiscoverableEntryPoint,
179-
// reserved: 0
180-
// )
181-
// )
182-
// }
183-
// return true
179+
}
180+
#endif
181+
182+
// Check if the name of the testing library the caller wants is equivalent to
183+
// "Swift Testing", ignoring case and punctuation. (If the caller did not
184+
// specify a library name, the caller wants records for all libraries.)
185+
let hint = hint.map { $0.load(as: UnsafePointer<CChar>.self) }
186+
if let hint {
187+
guard let hint = String(validatingCString: hint),
188+
String(hint.filter(\.isLetter)).lowercased() == "swifttesting" else {
189+
return false
190+
}
191+
}
192+
193+
// Initialize the provided memory to the (ABI-stable) library structure.
194+
_ = outValue.initializeMemory(
195+
as: SWTLibrary.self,
196+
to: .init(
197+
name: swt_getSwiftTestingLibraryName(),
198+
entryPoint: _discoverableEntryPoint,
199+
reserved: (0, 0, 0, 0, 0, 0)
200+
)
201+
)
202+
203+
return true
184204
}
185205

186206
#if compiler(>=6.3)
@@ -194,11 +214,22 @@ private func testingLibraryDiscoverableAccessor(_ outValue: UnsafeMutableRawPoin
194214
//@__testing(warning: "Platform-specific implementation missing: test content section name unavailable")
195215
#endif
196216
@used
217+
#else
218+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)
219+
@_section("__DATA_CONST,__swift5_tests")
220+
#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI)
221+
@_section("swift5_tests")
222+
#elseif os(Windows)
223+
@_section(".sw5test$B")
224+
#else
225+
//@__testing(warning: "Platform-specific implementation missing: test content section name unavailable")
226+
#endif
227+
@_used
228+
#endif
197229
private let testingLibraryRecord: __TestContentRecord = (
198230
0x6D61696E, /* 'main' */
199231
0,
200-
testingLibraryDiscoverableAccessor,
232+
_discoverableAccessor,
201233
0,
202234
0
203235
)
204-
#endif

Sources/Testing/ABI/EntryPoints/SwiftPMEntryPoint.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,6 @@ public func __swiftPMEntryPoint(passing args: __CommandLineArguments_v0? = nil)
5959
}
6060
#endif
6161

62-
// FIXME: this is probably the wrong layering for this check
63-
if let args = try? args ?? parseCommandLineArguments(from: CommandLine.arguments),
64-
let libraryName = args.testingLibrary,
65-
let library = Library(named: libraryName) {
66-
return await library.callEntryPoint(passing: args)
67-
}
68-
6962
return await entryPoint(passing: args, eventHandler: nil)
7063
}
7164

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
//
10+
11+
#if !defined(SWT_LIBRARY_H)
12+
#define SWT_LIBRARY_H
13+
14+
#include "Defines.h"
15+
#include "Includes.h"
16+
17+
SWT_ASSUME_NONNULL_BEGIN
18+
19+
/*
20+
fileprivate typealias EntryPoint = @Sendable @convention(c) (
21+
_ configurationJSON: UnsafeRawPointer,
22+
_ configurationJSONByteCount: Int,
23+
_ reserved: UInt,
24+
_ context: UnsafeRawPointer,
25+
_ recordJSONHandler: EntryPointRecordJSONHandler,
26+
_ completionHandler: EntryPointCompletionHandler
27+
) -> Void
28+
29+
fileprivate typealias EntryPointRecordJSONHandler = @Sendable @convention(c) (
30+
_ recordJSON: UnsafeRawPointer,
31+
_ recordJSONByteCount: Int,
32+
_ reserved: UInt,
33+
_ context: UnsafeRawPointer
34+
) -> Void
35+
36+
fileprivate typealias EntryPointCompletionHandler = @Sendable @convention(c) (
37+
_ exitCode: CInt,
38+
_ reserved: UInt,
39+
_ context: UnsafeRawPointer
40+
) -> Void
41+
*/
42+
43+
typedef void (* SWT_SENDABLE SWTLibraryEntryPointRecordJSONHandler)(
44+
const void *recordJSON,
45+
size_t recordJSONByteCount,
46+
uintptr_t reserved,
47+
const void *_Null_unspecified context
48+
);
49+
50+
typedef void (* SWT_SENDABLE SWTLibraryEntryPointCompletionHandler)(
51+
int exitCode,
52+
uintptr_t reserved,
53+
const void *_Null_unspecified context
54+
);
55+
56+
typedef void (* SWT_SENDABLE SWTLibraryEntryPoint)(
57+
const void *configurationJSON,
58+
size_t configurationJSONByteCount,
59+
uintptr_t reserved,
60+
const void *_Null_unspecified context,
61+
SWTLibraryEntryPointRecordJSONHandler SWT_SENDABLE recordJSONHandler,
62+
SWTLibraryEntryPointCompletionHandler SWT_SENDABLE completionHandler
63+
);
64+
65+
/// A C type that provides the in-memory layout of the ``Library`` Swift type.
66+
typedef struct SWTLibrary {
67+
const char *name;
68+
SWTLibraryEntryPoint SWT_SENDABLE entryPoint;
69+
uintptr_t reserved[6];
70+
} SWTLibrary;
71+
72+
/// Get the name of the testing library (i.e. `"Swift Testing"`) as a
73+
/// statically-allocated C string.
74+
static inline const char *swt_getSwiftTestingLibraryName(void) {
75+
return "Swift Testing";
76+
}
77+
78+
SWT_ASSUME_NONNULL_END
79+
80+
#endif

0 commit comments

Comments
 (0)