Skip to content

Commit e2528b9

Browse files
authored
Provide an opt-out conditional for lazy attachment encoding. (#811)
This PR provides a compile-time conditional, `SWT_NO_LAZY_ATTACHMENTS`, which when set causes Swift Testing's new experimental attachments feature to always eagerly encode/serialize/whatever attachments even if they're copyable and sendable. We anticipate using this functionality in Embedded Swift in the future. Note that support for Embedded Swift is not (yet!) a planned feature. ### "Why not make Attachment generic?" I've got a separate branch that tries to do this. It has a significantly more complex implementation and is brittle because we need to type-erase the attachable value in order to pass it into the event-handling machinery (which entails having a bunch of special-casing since `any Test.Attachable` does not conform to `Test.Attachable`.) `Attachment` is _already_ meant to represent a type-erased `Attachable` value. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent acf296d commit e2528b9

File tree

1 file changed

+53
-31
lines changed

1 file changed

+53
-31
lines changed

Sources/Testing/Attachments/Test.Attachment.swift

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,29 @@ extension Test {
2121
/// instance of ``Test/Attachment`` with that value and, optionally, a
2222
/// preferred filename to use when writing to disk.
2323
public struct Attachment: Sendable {
24+
#if !SWT_NO_LAZY_ATTACHMENTS
25+
/// Storage for ``attachableValue``.
26+
private var _attachableValue: any Attachable & Sendable /* & Copyable rdar://137614425 */
27+
2428
/// The value of this attachment.
2529
///
2630
/// The type of this property's value may not match the type of the value
2731
/// originally used to create this attachment.
28-
public var attachableValue: any Attachable & Sendable /* & Copyable rdar://137614425 */
32+
public var attachableValue: any Attachable & Sendable /* & Copyable rdar://137614425 */ {
33+
_attachableValue
34+
}
35+
#else
36+
/// Storage for ``attachableValue``.
37+
private var _attachableValue: _AttachableProxy
2938

30-
/// The source location where the attachment was initialized.
39+
/// The value of this attachment.
3140
///
32-
/// The value of this property is used when recording issues associated with
33-
/// the attachment.
34-
public var sourceLocation: SourceLocation
35-
36-
/// The default preferred name to use if the developer does not supply one.
37-
package static var defaultPreferredName: String {
38-
"untitled"
41+
/// The type of this property's value may not match the type of the value
42+
/// originally used to create this attachment.
43+
public var attachableValue: some Test.Attachable & Sendable & Copyable {
44+
_attachableValue
3945
}
46+
#endif
4047

4148
/// The path to which the this attachment was written, if any.
4249
///
@@ -51,26 +58,9 @@ extension Test {
5158
@_spi(ForToolsIntegrationOnly)
5259
public var fileSystemPath: String?
5360

54-
/// Initialize an instance of this type that encloses the given attachable
55-
/// value.
56-
///
57-
/// - Parameters:
58-
/// - attachableValue: The value that will be attached to the output of
59-
/// the test run.
60-
/// - preferredName: The preferred name of the attachment when writing it
61-
/// to a test report or to disk. If `nil`, the testing library attempts
62-
/// to derive a reasonable filename for the attached value.
63-
/// - sourceLocation: The source location of the call to this initializer.
64-
/// This value is used when recording issues associated with the
65-
/// attachment.
66-
public init(
67-
_ attachableValue: some Attachable & Sendable & Copyable,
68-
named preferredName: String? = nil,
69-
sourceLocation: SourceLocation = #_sourceLocation
70-
) {
71-
self.attachableValue = attachableValue
72-
self.preferredName = preferredName ?? Self.defaultPreferredName
73-
self.sourceLocation = sourceLocation
61+
/// The default preferred name to use if the developer does not supply one.
62+
package static var defaultPreferredName: String {
63+
"untitled"
7464
}
7565

7666
/// A filename to use when writing this attachment to a test report or to a
@@ -81,12 +71,41 @@ extension Test {
8171
/// value of this property has not been explicitly set, the testing library
8272
/// will attempt to generate its own value.
8373
public var preferredName: String
74+
75+
/// The source location where the attachment was initialized.
76+
///
77+
/// The value of this property is used when recording issues associated with
78+
/// the attachment.
79+
public var sourceLocation: SourceLocation
8480
}
8581
}
8682

8783
// MARK: -
8884

8985
extension Test.Attachment {
86+
#if !SWT_NO_LAZY_ATTACHMENTS
87+
/// Initialize an instance of this type that encloses the given attachable
88+
/// value.
89+
///
90+
/// - Parameters:
91+
/// - attachableValue: The value that will be attached to the output of
92+
/// the test run.
93+
/// - preferredName: The preferred name of the attachment when writing it
94+
/// to a test report or to disk. If `nil`, the testing library attempts
95+
/// to derive a reasonable filename for the attached value.
96+
/// - sourceLocation: The source location of the call to this initializer.
97+
/// This value is used when recording issues associated with the
98+
/// attachment.
99+
public init(
100+
_ attachableValue: some Test.Attachable & Sendable & Copyable,
101+
named preferredName: String? = nil,
102+
sourceLocation: SourceLocation = #_sourceLocation
103+
) {
104+
let preferredName = preferredName ?? Self.defaultPreferredName
105+
self.init(_attachableValue: attachableValue, preferredName: preferredName, sourceLocation: sourceLocation)
106+
}
107+
#endif
108+
90109
/// Attach this instance to the current test.
91110
///
92111
/// An attachment can only be attached once.
@@ -129,19 +148,22 @@ extension Test.Attachment {
129148
/// value cannot be encoded and an error is thrown, that error is recorded as
130149
/// an issue in the current test and the resulting instance of
131150
/// ``Test/Attachment`` is empty.
151+
#if !SWT_NO_LAZY_ATTACHMENTS
132152
@_disfavoredOverload
153+
#endif
133154
public init(
134155
_ attachableValue: borrowing some Test.Attachable & ~Copyable,
135156
named preferredName: String? = nil,
136157
sourceLocation: SourceLocation = #_sourceLocation
137158
) {
159+
let preferredName = preferredName ?? Self.defaultPreferredName
138160
var proxyAttachable = _AttachableProxy()
139161
proxyAttachable.estimatedAttachmentByteCount = attachableValue.estimatedAttachmentByteCount
140162

141163
// BUG: the borrow checker thinks that withErrorRecording() is consuming
142164
// attachableValue, so get around it with an additional do/catch clause.
143165
do {
144-
let proxyAttachment = Self(proxyAttachable, named: preferredName, sourceLocation: sourceLocation)
166+
let proxyAttachment = Self(_attachableValue: proxyAttachable, preferredName: preferredName, sourceLocation: sourceLocation)
145167
proxyAttachable.encodedValue = try attachableValue.withUnsafeBufferPointer(for: proxyAttachment) { buffer in
146168
[UInt8](buffer)
147169
}
@@ -155,7 +177,7 @@ extension Test.Attachment {
155177
}
156178
}
157179

158-
self.init(proxyAttachable, named: preferredName, sourceLocation: sourceLocation)
180+
self.init(_attachableValue: proxyAttachable, preferredName: preferredName, sourceLocation: sourceLocation)
159181
}
160182
}
161183

0 commit comments

Comments
 (0)