@@ -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 {
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
+
27
41
/// Storage for ``attachableValue-7dyjv``.
28
- fileprivate var _attachableValue : AttachableValue
42
+ private 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,29 +118,12 @@ 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
}
112
125
}
113
126
114
- @_spi ( ForToolsIntegrationOnly)
115
- extension Attachment where AttachableValue == AnyAttachable {
116
- /// Create a type-erased attachment from an instance of ``Attachment``.
117
- ///
118
- /// - Parameters:
119
- /// - attachment: The attachment to type-erase.
120
- fileprivate init ( _ attachment: Attachment < some Attachable & Sendable & Copyable > ) {
121
- self . init (
122
- _attachableValue: AnyAttachable ( wrappedValue: attachment. attachableValue) ,
123
- fileSystemPath: attachment. fileSystemPath,
124
- _preferredName: attachment. _preferredName,
125
- sourceLocation: attachment. sourceLocation
126
- )
127
- }
128
- }
129
- #endif
130
-
131
127
/// A type-erased wrapper type that represents any attachable value.
132
128
///
133
129
/// This type is not generally visible to developers. It is used when posting
@@ -140,47 +136,45 @@ extension Attachment where AttachableValue == AnyAttachable {
140
136
/// `Event.Kind.valueAttached(_:)`, otherwise it would be declared private.
141
137
/// }
142
138
@_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
139
+ public struct AnyAttachable : AttachableWrapper , Sendable , Copyable {
140
+ public struct Wrapped : Sendable { }
149
141
150
- public var wrappedValue : Wrapped
142
+ public var wrappedValue : Wrapped {
143
+ Wrapped ( )
144
+ }
151
145
152
- init ( wrappedValue: Wrapped ) {
153
- self . wrappedValue = wrappedValue
146
+ init < A> ( _ attachment: Attachment < A > ) where A: Attachable & Sendable & ~ Copyable {
147
+ _estimatedAttachmentByteCount = { attachment. attachableValue. estimatedAttachmentByteCount }
148
+ _withUnsafeBytes = { try attachment. withUnsafeBytes ( $0) }
149
+ _preferredName = { attachment. attachableValue. preferredName ( for: attachment, basedOn: $0) }
154
150
}
155
151
152
+ /// The implementation of ``estimatedAttachmentByteCount`` borrowed from the
153
+ /// original attachment.
154
+ private var _estimatedAttachmentByteCount : @Sendable ( ) -> Int ?
155
+
156
156
public var estimatedAttachmentByteCount : Int ? {
157
- wrappedValue . estimatedAttachmentByteCount
157
+ _estimatedAttachmentByteCount ( )
158
158
}
159
159
160
+ /// The implementation of ``withUnsafeBytes(for:_:)`` borrowed from the
161
+ /// original attachment.
162
+ private var _withUnsafeBytes : @Sendable ( ( UnsafeRawBufferPointer ) throws -> Void ) throws -> Void
163
+
160
164
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)
165
+ var result : R !
166
+ try _withUnsafeBytes { bytes in
167
+ result = try body ( bytes)
169
168
}
170
- return try open ( wrappedValue , for : attachment )
169
+ return result
171
170
}
172
171
172
+ /// The implementation of ``preferredName(for:basedOn:)`` borrowed from the
173
+ /// original attachment.
174
+ private var _preferredName : @Sendable ( String ) -> String
175
+
173
176
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)
177
+ _preferredName ( suggestedName)
184
178
}
185
179
}
186
180
@@ -215,7 +209,7 @@ extension Attachment where AttachableValue: ~Copyable {
215
209
/// }
216
210
@_disfavoredOverload public var attachableValue : AttachableValue {
217
211
_read {
218
- yield _attachableValue
212
+ yield _storage . attachableValue
219
213
}
220
214
}
221
215
}
@@ -245,32 +239,33 @@ extension Attachment where AttachableValue: AttachableWrapper & ~Copyable {
245
239
246
240
// MARK: - Attaching an attachment to a test (etc.)
247
241
248
- #if !SWT_NO_LAZY_ATTACHMENTS
249
- extension Attachment where AttachableValue: Sendable & Copyable {
242
+ extension Attachment where AttachableValue: Sendable & ~ Copyable {
250
243
/// Attach an attachment to the current test.
251
244
///
252
245
/// - Parameters:
253
246
/// - attachment: The attachment to attach.
254
247
/// - sourceLocation: The source location of the call to this function.
255
248
///
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.
249
+ /// When `attachableValue` is an instance of a type that does not conform to
250
+ /// the [`Sendable`](https://developer.apple.com/documentation/swift/sendable)
251
+ /// protocol, the testing library encodes it as data immediately. If
252
+ /// `attachableValue` throws an error when the testing library attempts to
253
+ /// encode it, the testing library records that error as an issue in the
254
+ /// current test and does not write the attachment to the test report or to
255
+ /// persistent storage.
265
256
///
266
257
/// @Metadata {
267
258
/// @Available(Swift, introduced: 6.2)
268
259
/// @Available(Xcode, introduced: 26.0)
269
260
/// }
270
261
@_documentation ( visibility: private)
271
262
public static func record( _ attachment: consuming Self , sourceLocation: SourceLocation = #_sourceLocation) {
272
- var attachmentCopy = Attachment < AnyAttachable > ( attachment)
273
- attachmentCopy. sourceLocation = sourceLocation
263
+ var attachmentCopy = Attachment < AnyAttachable > (
264
+ AnyAttachable ( copy attachment) ,
265
+ named: attachment. _preferredName,
266
+ sourceLocation: sourceLocation
267
+ )
268
+ attachmentCopy. fileSystemPath = attachment. fileSystemPath
274
269
Event . post ( . valueAttached( attachmentCopy) )
275
270
}
276
271
@@ -283,19 +278,17 @@ extension Attachment where AttachableValue: Sendable & Copyable {
283
278
/// derive a reasonable filename for the attached value.
284
279
/// - sourceLocation: The source location of the call to this function.
285
280
///
286
- /// When attaching a value of a type that does not conform to both
287
- /// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
288
- /// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
289
- /// the testing library encodes it as data immediately. If the value cannot be
290
- /// encoded and an error is thrown, that error is recorded as an issue in the
291
- /// current test and the attachment is not written to the test report or to
292
- /// disk .
281
+ /// When `attachableValue` is an instance of a type that does not conform to
282
+ /// the [`Sendable`](https://developer.apple.com/documentation/swift/sendable)
283
+ /// protocol, the testing library encodes it as data immediately. If
284
+ /// `attachableValue` throws an error when the testing library attempts to
285
+ /// encode it, the testing library records that error as an issue in the
286
+ /// current test and does not write the attachment to the test report or to
287
+ /// persistent storage .
293
288
///
294
289
/// This function creates a new instance of ``Attachment`` and immediately
295
290
/// attaches it to the current test.
296
291
///
297
- /// An attachment can only be attached once.
298
- ///
299
292
/// @Metadata {
300
293
/// @Available(Swift, introduced: 6.2)
301
294
/// @Available(Xcode, introduced: 26.0)
@@ -305,7 +298,6 @@ extension Attachment where AttachableValue: Sendable & Copyable {
305
298
record ( Self ( attachableValue, named: preferredName, sourceLocation: sourceLocation) , sourceLocation: sourceLocation)
306
299
}
307
300
}
308
- #endif
309
301
310
302
extension Attachment where AttachableValue: ~ Copyable {
311
303
/// Attach an attachment to the current test.
@@ -314,32 +306,22 @@ extension Attachment where AttachableValue: ~Copyable {
314
306
/// - attachment: The attachment to attach.
315
307
/// - sourceLocation: The source location of the call to this function.
316
308
///
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.
309
+ /// When `attachableValue` is an instance of a type that does not conform to
310
+ /// the [`Sendable`](https://developer.apple.com/documentation/swift/sendable)
311
+ /// protocol, the testing library encodes it as data immediately. If
312
+ /// `attachableValue` throws an error when the testing library attempts to
313
+ /// encode it, the testing library records that error as an issue in the
314
+ /// current test and does not write the attachment to the test report or to
315
+ /// persistent storage.
326
316
///
327
317
/// @Metadata {
328
318
/// @Available(Swift, introduced: 6.2)
329
319
/// @Available(Xcode, introduced: 26.0)
330
320
/// }
331
321
public static func record( _ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) {
332
322
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) )
323
+ let bufferCopy = try attachment. withUnsafeBytes { Array ( $0) }
324
+ Attachment< Array> . record( bufferCopy, sourceLocation: sourceLocation)
343
325
} catch {
344
326
let sourceContext = SourceContext ( backtrace: . current( ) , sourceLocation: sourceLocation)
345
327
Issue ( kind: . valueAttachmentFailed( error) , comments: [ ] , sourceContext: sourceContext) . record ( )
@@ -355,19 +337,17 @@ extension Attachment where AttachableValue: ~Copyable {
355
337
/// derive a reasonable filename for the attached value.
356
338
/// - sourceLocation: The source location of the call to this function.
357
339
///
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 .
340
+ /// When `attachableValue` is an instance of a type that does not conform to
341
+ /// the [`Sendable`](https://developer.apple.com/documentation/swift/sendable)
342
+ /// protocol, the testing library encodes it as data immediately. If
343
+ /// `attachableValue` throws an error when the testing library attempts to
344
+ /// encode it, the testing library records that error as an issue in the
345
+ /// current test and does not write the attachment to the test report or to
346
+ /// persistent storage .
365
347
///
366
348
/// This function creates a new instance of ``Attachment`` and immediately
367
349
/// attaches it to the current test.
368
350
///
369
- /// An attachment can only be attached once.
370
- ///
371
351
/// @Metadata {
372
352
/// @Available(Swift, introduced: 6.2)
373
353
/// @Available(Xcode, introduced: 26.0)
0 commit comments