@@ -23,9 +23,23 @@ private import _TestingInternals
23
23
/// @Available(Swift, introduced: 6.2)
24
24
/// @Available(Xcode, introduced: 26.0)
25
25
/// }
26
- public struct Attachment < AttachableValue> : ~ Copyable where AttachableValue: Attachable & ~ Copyable {
27
- /// Storage for ``attachableValue-7dyjv``.
28
- fileprivate var _attachableValue : AttachableValue
26
+ public struct Attachment < AttachableValue> where AttachableValue: Attachable & ~ Copyable {
27
+ /// A class that stores an attachment's (potentially move-only) attachable
28
+ /// value.
29
+ ///
30
+ /// We use a class to store the attachable value so that ``Attachment`` can
31
+ /// conform to `Copyable` even if `AttachableValue` doesn't.
32
+ fileprivate final class Storage {
33
+ /// Storage for ``Attachment/attachableValue-7dyjv``.
34
+ let attachableValue : AttachableValue
35
+
36
+ init ( _ attachableValue: consuming AttachableValue ) {
37
+ self . attachableValue = attachableValue
38
+ }
39
+ }
40
+
41
+ /// Storage for ``attachableValue``.
42
+ fileprivate var storage : Storage
29
43
30
44
/// The path to which the this attachment was written, if any.
31
45
///
@@ -80,12 +94,11 @@ public struct Attachment<AttachableValue>: ~Copyable where AttachableValue: Atta
80
94
var sourceLocation : SourceLocation
81
95
}
82
96
83
- extension Attachment : Copyable where AttachableValue: Copyable { }
84
97
extension Attachment : Sendable where AttachableValue: Sendable { }
98
+ extension Attachment . Storage : Sendable where AttachableValue: Sendable { }
85
99
86
100
// MARK: - Initializing an attachment
87
101
88
- #if !SWT_NO_LAZY_ATTACHMENTS
89
102
extension Attachment where AttachableValue: ~ Copyable {
90
103
/// Initialize an instance of this type that encloses the given attachable
91
104
/// value.
@@ -105,7 +118,7 @@ extension Attachment where AttachableValue: ~Copyable {
105
118
/// @Available(Xcode, introduced: 26.0)
106
119
/// }
107
120
public init ( _ attachableValue: consuming AttachableValue , named preferredName: String ? = nil , sourceLocation: SourceLocation = #_sourceLocation) {
108
- self . _attachableValue = attachableValue
121
+ self . storage = Storage ( attachableValue)
109
122
self . _preferredName = preferredName
110
123
self . sourceLocation = sourceLocation
111
124
}
@@ -117,16 +130,15 @@ extension Attachment where AttachableValue == AnyAttachable {
117
130
///
118
131
/// - Parameters:
119
132
/// - attachment: The attachment to type-erase.
120
- fileprivate init ( _ attachment: Attachment < some Attachable & Sendable & Copyable > ) {
133
+ fileprivate init ( _ attachment: Attachment < some Attachable & Sendable & ~ Copyable> ) {
121
134
self . init (
122
- _attachableValue : AnyAttachable ( wrappedValue : attachment. attachableValue ) ,
135
+ storage : Storage ( AnyAttachable ( attachment) ) ,
123
136
fileSystemPath: attachment. fileSystemPath,
124
137
_preferredName: attachment. _preferredName,
125
138
sourceLocation: attachment. sourceLocation
126
139
)
127
140
}
128
141
}
129
- #endif
130
142
131
143
/// A type-erased wrapper type that represents any attachable value.
132
144
///
@@ -140,47 +152,45 @@ extension Attachment where AttachableValue == AnyAttachable {
140
152
/// `Event.Kind.valueAttached(_:)`, otherwise it would be declared private.
141
153
/// }
142
154
@_spi ( ForToolsIntegrationOnly)
143
- public struct AnyAttachable : AttachableWrapper , Copyable , Sendable {
144
- #if !SWT_NO_LAZY_ATTACHMENTS
145
- public typealias Wrapped = any Attachable & Sendable /* & Copyable rdar://137614425 */
146
- #else
147
- public typealias Wrapped = [ UInt8 ]
148
- #endif
155
+ public struct AnyAttachable : AttachableWrapper , Sendable , Copyable {
156
+ public struct Wrapped : Sendable { }
149
157
150
- public var wrappedValue : Wrapped
158
+ public var wrappedValue : Wrapped {
159
+ Wrapped ( )
160
+ }
151
161
152
- init ( wrappedValue: Wrapped ) {
153
- self . wrappedValue = wrappedValue
162
+ init < A> ( _ attachment: Attachment < A > ) where A: Attachable & Sendable & ~ Copyable {
163
+ _estimatedAttachmentByteCount = { attachment. attachableValue. estimatedAttachmentByteCount }
164
+ _withUnsafeBytes = { try attachment. withUnsafeBytes ( $0) }
165
+ _preferredName = { attachment. attachableValue. preferredName ( for: attachment, basedOn: $0) }
154
166
}
155
167
168
+ /// The implementation of ``estimatedAttachmentByteCount`` borrowed from the
169
+ /// original attachment.
170
+ private var _estimatedAttachmentByteCount : @Sendable ( ) -> Int ?
171
+
156
172
public var estimatedAttachmentByteCount : Int ? {
157
- wrappedValue . estimatedAttachmentByteCount
173
+ _estimatedAttachmentByteCount ( )
158
174
}
159
175
176
+ /// The implementation of ``withUnsafeBytes(for:_:)`` borrowed from the
177
+ /// original attachment.
178
+ private var _withUnsafeBytes : @Sendable ( ( UnsafeRawBufferPointer ) throws -> Void ) throws -> Void
179
+
160
180
public func withUnsafeBytes< R> ( for attachment: borrowing Attachment < Self > , _ body: ( UnsafeRawBufferPointer ) throws -> R ) throws -> R {
161
- func open< T> ( _ wrappedValue: T , for attachment: borrowing Attachment < Self > ) throws -> R where T: Attachable & Sendable & Copyable {
162
- let temporaryAttachment = Attachment < T > (
163
- _attachableValue: wrappedValue,
164
- fileSystemPath: attachment. fileSystemPath,
165
- _preferredName: attachment. _preferredName,
166
- sourceLocation: attachment. sourceLocation
167
- )
168
- return try temporaryAttachment. withUnsafeBytes ( body)
181
+ var result : R !
182
+ try _withUnsafeBytes { bytes in
183
+ result = try body ( bytes)
169
184
}
170
- return try open ( wrappedValue , for : attachment )
185
+ return result
171
186
}
172
187
188
+ /// The implementation of ``preferredName(for:basedOn:)`` borrowed from the
189
+ /// original attachment.
190
+ private var _preferredName : @Sendable ( String ) -> String
191
+
173
192
public borrowing func preferredName( for attachment: borrowing Attachment < Self > , basedOn suggestedName: String ) -> String {
174
- func open< T> ( _ wrappedValue: T , for attachment: borrowing Attachment < Self > ) -> String where T: Attachable & Sendable & Copyable {
175
- let temporaryAttachment = Attachment < T > (
176
- _attachableValue: wrappedValue,
177
- fileSystemPath: attachment. fileSystemPath,
178
- _preferredName: attachment. _preferredName,
179
- sourceLocation: attachment. sourceLocation
180
- )
181
- return temporaryAttachment. preferredName
182
- }
183
- return open ( wrappedValue, for: attachment)
193
+ _preferredName ( suggestedName)
184
194
}
185
195
}
186
196
@@ -215,7 +225,7 @@ extension Attachment where AttachableValue: ~Copyable {
215
225
/// }
216
226
@_disfavoredOverload public var attachableValue : AttachableValue {
217
227
_read {
218
- yield _attachableValue
228
+ yield storage . attachableValue
219
229
}
220
230
}
221
231
}
@@ -245,23 +255,20 @@ extension Attachment where AttachableValue: AttachableWrapper & ~Copyable {
245
255
246
256
// MARK: - Attaching an attachment to a test (etc.)
247
257
248
- #if !SWT_NO_LAZY_ATTACHMENTS
249
- extension Attachment where AttachableValue: Sendable & Copyable {
258
+ extension Attachment where AttachableValue: Sendable & ~ Copyable {
250
259
/// Attach an attachment to the current test.
251
260
///
252
261
/// - Parameters:
253
262
/// - attachment: The attachment to attach.
254
263
/// - sourceLocation: The source location of the call to this function.
255
264
///
256
- /// When attaching a value of a type that does not conform to both
257
- /// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
258
- /// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
259
- /// the testing library encodes it as data immediately. If the value cannot be
260
- /// encoded and an error is thrown, that error is recorded as an issue in the
261
- /// current test and the attachment is not written to the test report or to
262
- /// disk.
263
- ///
264
- /// An attachment can only be attached once.
265
+ /// When `attachableValue` is an instance of a type that does not conform to
266
+ /// the [`Sendable`](https://developer.apple.com/documentation/swift/sendable)
267
+ /// protocol, the testing library encodes it as data immediately. If
268
+ /// `attachableValue` throws an error when the testing library attempts to
269
+ /// encode it, the testing library records that error as an issue in the
270
+ /// current test and does not write the attachment to the test report or to
271
+ /// persistent storage.
265
272
///
266
273
/// @Metadata {
267
274
/// @Available(Swift, introduced: 6.2)
@@ -294,8 +301,6 @@ extension Attachment where AttachableValue: Sendable & Copyable {
294
301
/// This function creates a new instance of ``Attachment`` and immediately
295
302
/// attaches it to the current test.
296
303
///
297
- /// An attachment can only be attached once.
298
- ///
299
304
/// @Metadata {
300
305
/// @Available(Swift, introduced: 6.2)
301
306
/// @Available(Xcode, introduced: 26.0)
@@ -305,7 +310,6 @@ extension Attachment where AttachableValue: Sendable & Copyable {
305
310
record ( Self ( attachableValue, named: preferredName, sourceLocation: sourceLocation) , sourceLocation: sourceLocation)
306
311
}
307
312
}
308
- #endif
309
313
310
314
extension Attachment where AttachableValue: ~ Copyable {
311
315
/// Attach an attachment to the current test.
@@ -314,32 +318,21 @@ extension Attachment where AttachableValue: ~Copyable {
314
318
/// - attachment: The attachment to attach.
315
319
/// - sourceLocation: The source location of the call to this function.
316
320
///
317
- /// When attaching a value of a type that does not conform to both
318
- /// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
319
- /// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
320
- /// the testing library encodes it as data immediately. If the value cannot be
321
- /// encoded and an error is thrown, that error is recorded as an issue in the
322
- /// current test and the attachment is not written to the test report or to
323
- /// disk.
324
- ///
325
- /// An attachment can only be attached once.
321
+ /// When attaching a value of a type that does not conform to the
322
+ /// [`Sendable`](https://developer.apple.com/documentation/swift/sendable)
323
+ /// protocol, the testing library encodes it as data immediately. If the value
324
+ /// cannot be encoded and an error is thrown, that error is recorded as an
325
+ /// issue in the current test and the attachment is not written to the test
326
+ /// report or to disk.
326
327
///
327
328
/// @Metadata {
328
329
/// @Available(Swift, introduced: 6.2)
329
330
/// @Available(Xcode, introduced: 26.0)
330
331
/// }
331
332
public static func record( _ attachment: consuming Self , sourceLocation: SourceLocation = #_sourceLocation) {
332
333
do {
333
- let attachmentCopy = try attachment. withUnsafeBytes { buffer in
334
- let attachableWrapper = AnyAttachable ( wrappedValue: Array ( buffer) )
335
- return Attachment < AnyAttachable > (
336
- _attachableValue: attachableWrapper,
337
- fileSystemPath: attachment. fileSystemPath,
338
- _preferredName: attachment. preferredName, // invokes preferredName(for:basedOn:)
339
- sourceLocation: sourceLocation
340
- )
341
- }
342
- Event . post ( . valueAttached( attachmentCopy) )
334
+ let bufferCopy = try attachment. withUnsafeBytes { Array ( $0) }
335
+ Attachment< Array> . record( bufferCopy, sourceLocation: sourceLocation)
343
336
} catch {
344
337
let sourceContext = SourceContext ( backtrace: . current( ) , sourceLocation: sourceLocation)
345
338
Issue ( kind: . valueAttachmentFailed( error) , comments: [ ] , sourceContext: sourceContext) . record ( )
@@ -355,19 +348,17 @@ extension Attachment where AttachableValue: ~Copyable {
355
348
/// derive a reasonable filename for the attached value.
356
349
/// - sourceLocation: The source location of the call to this function.
357
350
///
358
- /// When attaching a value of a type that does not conform to both
359
- /// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
360
- /// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
361
- /// the testing library encodes it as data immediately. If the value cannot be
362
- /// encoded and an error is thrown, that error is recorded as an issue in the
363
- /// current test and the attachment is not written to the test report or to
364
- /// disk .
351
+ /// When `attachableValue` is an instance of a type that does not conform to
352
+ /// the [`Sendable`](https://developer.apple.com/documentation/swift/sendable)
353
+ /// protocol, the testing library encodes it as data immediately. If
354
+ /// `attachableValue` throws an error when the testing library attempts to
355
+ /// encode it, the testing library records that error as an issue in the
356
+ /// current test and does not write the attachment to the test report or to
357
+ /// persistent storage .
365
358
///
366
359
/// This function creates a new instance of ``Attachment`` and immediately
367
360
/// attaches it to the current test.
368
361
///
369
- /// An attachment can only be attached once.
370
- ///
371
362
/// @Metadata {
372
363
/// @Available(Swift, introduced: 6.2)
373
364
/// @Available(Xcode, introduced: 26.0)
0 commit comments