Skip to content

Commit 04c8058

Browse files
committed
Relax the alignment requirement for DiscoverableAsTestContent.Context.
This PR allows `DiscoverableAsTestContent.Context` to be less-aligned than `UInt` so long as its stride remains the same. It also removes the sneaky conformance of `ExitTest` to `DiscoverableAsTestContent`, opting instead to use an internal type. Since the conformance to `DiscoverableAsTestContent` and the implementation of `__store()` form a closed system (where all type information is controlled by Swift Testing at runtime), we can do this without breaking any ABI. I've updated ABI/TestContent.md to remove some of the relevant implementation details.
1 parent 55f82ed commit 04c8058

File tree

5 files changed

+41
-43
lines changed

5 files changed

+41
-43
lines changed

Documentation/ABI/TestContent.md

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -149,25 +149,12 @@ The fourth argument to this function, `reserved`, is reserved for future use.
149149
Accessor functions should assume it is `0` and must not access it.
150150

151151
The concrete Swift type of the value written to `outValue`, the type pointed to
152-
by `type`, and the value pointed to by `hint` depend on the kind of record:
152+
by `type`, and the value pointed to by `hint` depend on the kind of record.
153153

154-
- For test or suite declarations (kind `0x74657374`), the accessor produces a
155-
structure of type `Testing.Test.Generator` that the testing library can use
156-
to generate the corresponding test[^notAccessorSignature].
157-
158-
[^notAccessorSignature]: This level of indirection is necessary because
159-
loading a test or suite declaration is an asynchronous operation, but C
160-
functions cannot be `async`.
161-
162-
Test content records of this kind do not specify a type for `hint`. Always
163-
pass `nil`.
164-
165-
- For exit test declarations (kind `0x65786974`), the accessor produces a
166-
structure describing the exit test (of type `Testing.ExitTest`.)
167-
168-
Test content records of this kind accept a `hint` of type `Testing.ExitTest.ID`.
169-
They only produce a result if they represent an exit test declared with the
170-
same ID (or if `hint` is `nil`.)
154+
The record kinds defined by Swift Testing (kinds `0x74657374` and `0x65786974`)
155+
make use of the `DiscoverableAsTestContent` protocol in the `_TestDiscovery`
156+
module and do not publicly expose the types of their accessor functions'
157+
arguments. Do not call the accessor functions for these records directly.
171158

172159
> [!WARNING]
173160
> Calling code should use [`withUnsafeTemporaryAllocation(of:capacity:_:)`](https://developer.apple.com/documentation/swift/withunsafetemporaryallocation(of:capacity:_:))
@@ -274,7 +261,8 @@ extension FoodTruckDiagnostic: DiscoverableAsTestContent {
274261
```
275262

276263
If you customize `TestContentContext`, be aware that the type you specify must
277-
have the same stride and alignment as `UInt`.
264+
have the same stride as `UInt` and must have an alignment less than or equal to
265+
that of `UInt`.
278266

279267
When you are done configuring your type's protocol conformance, you can then
280268
enumerate all test content records matching it as instances of

Sources/Testing/Discovery+Macro.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,6 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11-
@_spi(Experimental) @_spi(ForToolsIntegrationOnly) internal import _TestDiscovery
12-
13-
/// A shadow declaration of `_TestDiscovery.DiscoverableAsTestContent` that
14-
/// allows us to add public conformances to it without causing the
15-
/// `_TestDiscovery` module to appear in `Testing.private.swiftinterface`.
16-
///
17-
/// This protocol is not part of the public interface of the testing library.
18-
protocol DiscoverableAsTestContent: _TestDiscovery.DiscoverableAsTestContent, ~Copyable {}
19-
2011
/// The type of the accessor function used to access a test content record.
2112
///
2213
/// The signature of this function type must match that of the corresponding

Sources/Testing/ExitTests/ExitTest.swift

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,33 @@ extension ExitTest {
258258

259259
// MARK: - Discovery
260260

261-
extension ExitTest: DiscoverableAsTestContent {
262-
fileprivate static var testContentKind: TestContentKind {
263-
"exit"
264-
}
261+
extension ExitTest {
262+
/// A type representing an exit test as a test content record.
263+
fileprivate struct Record: Sendable, DiscoverableAsTestContent {
264+
static var testContentKind: TestContentKind {
265+
"exit"
266+
}
267+
268+
typealias TestContentAccessorHint = ID
269+
270+
/// The ID of the represented exit test.
271+
private var _id: ExitTest.ID
272+
273+
/// The body of the represented exit test.
274+
private var _body: @Sendable () async throws -> Void
265275

266-
fileprivate typealias TestContentAccessorHint = ID
276+
init(id: ExitTest.ID, body: @escaping @Sendable () async throws -> Void) {
277+
_id = id
278+
_body = body
279+
}
280+
281+
/// Make the exit test represented by this instance.
282+
///
283+
/// - Returns: A new exit test as represented by this instance.
284+
func makeExitTest() -> ExitTest {
285+
ExitTest(id: _id, body: _body)
286+
}
287+
}
267288

268289
/// Store the exit test into the given memory.
269290
///
@@ -287,17 +308,15 @@ extension ExitTest: DiscoverableAsTestContent {
287308
withHintAt hintAddress: UnsafeRawPointer? = nil
288309
) -> CBool {
289310
#if !hasFeature(Embedded)
290-
let callerExpectedType = TypeInfo(describing: typeAddress.load(as: Any.Type.self))
291-
let selfType = TypeInfo(describing: Self.self)
292-
guard callerExpectedType == selfType else {
311+
guard typeAddress.load(as: Any.Type.self) == Record.self else {
293312
return false
294313
}
295314
#endif
296315
let id = ID(id)
297316
if let hintedID = hintAddress?.load(as: ID.self), hintedID != id {
298317
return false
299318
}
300-
outValue.initializeMemory(as: Self.self, to: Self(id: id, body: body))
319+
outValue.initializeMemory(as: Record.self, to: Record(id: id, body: body))
301320
return true
302321
}
303322
}
@@ -312,16 +331,16 @@ extension ExitTest {
312331
/// - Returns: The specified exit test function, or `nil` if no such exit test
313332
/// could be found.
314333
public static func find(identifiedBy id: ExitTest.ID) -> Self? {
315-
for record in Self.allTestContentRecords() {
316-
if let exitTest = record.load(withHint: id) {
334+
for record in Record.allTestContentRecords() {
335+
if let exitTest = record.load(withHint: id)?.makeExitTest() {
317336
return exitTest
318337
}
319338
}
320339

321340
#if !SWT_NO_LEGACY_TEST_DISCOVERY
322341
// Call the legacy lookup function that discovers tests embedded in types.
323-
for record in Self.allTypeMetadataBasedTestContentRecords() {
324-
if let exitTest = record.load(withHint: id) {
342+
for record in Record.allTypeMetadataBasedTestContentRecords() {
343+
if let exitTest = record.load(withHint: id)?.makeExitTest() {
325344
return exitTest
326345
}
327346
}

Sources/_TestDiscovery/TestContentRecord.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ extension DiscoverableAsTestContent where Self: ~Copyable {
5252
/// ([swift-#79667](https://github.com/swiftlang/swift/issues/79667))
5353
fileprivate static func validateMemoryLayout() {
5454
precondition(MemoryLayout<TestContentContext>.stride == MemoryLayout<UInt>.stride, "'\(self).TestContentContext' aka '\(TestContentContext.self)' must have the same stride as 'UInt'.")
55-
precondition(MemoryLayout<TestContentContext>.alignment == MemoryLayout<UInt>.alignment, "'\(self).TestContentContext' aka '\(TestContentContext.self)' must have the same alignment as 'UInt'.")
55+
precondition(MemoryLayout<TestContentContext>.alignment <= MemoryLayout<UInt>.alignment, "'\(self).TestContentContext' aka '\(TestContentContext.self)' must have an alignment less than or equal to that of 'UInt'.")
5656
}
5757
}
5858

Tests/TestingTests/DiscoveryTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ struct DiscoveryTests {
5959
#endif
6060

6161
#if !SWT_NO_DYNAMIC_LINKING && hasFeature(SymbolLinkageMarkers)
62-
struct MyTestContent: Testing.DiscoverableAsTestContent {
62+
struct MyTestContent: DiscoverableAsTestContent {
6363
typealias TestContentAccessorHint = UInt32
6464

6565
var value: UInt32

0 commit comments

Comments
 (0)