Skip to content

Commit 588807a

Browse files
authored
Change Attachment.attach() to Attachment.record() and Attachable.withUnsafeBufferPointer() to Attachable.withUnsafeBytes(). (#1032)
This PR updates the `attach()` function to make it static on `Attachment` and renames it `record()`, per the discussion [here](https://forums.swift.org/t/pitch-attachments/78072). It also renames `Attachable.withUnsafeBufferPointer()` to `Attachable.withUnsafeBytes()` to more closely match the convention used by the standard library for raw buffers. Finally, it removes the conditional dependence on the experimental language feature "SuppressedAssociatedTypes" in `AttachableContainer`. ### 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 65ca016 commit 588807a

File tree

12 files changed

+134
-77
lines changed

12 files changed

+134
-77
lines changed

Package.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ extension Array where Element == PackageDescription.SwiftSetting {
197197

198198
result += [
199199
.enableUpcomingFeature("ExistentialAny"),
200-
.enableExperimentalFeature("SuppressedAssociatedTypes"),
201200

202201
.enableExperimentalFeature("AccessLevelOnImport"),
203202
.enableUpcomingFeature("InternalImportsByDefault"),

Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageContainer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import UniformTypeIdentifiers
3232
/// such a requirement, and all image types we care about are non-final
3333
/// classes. Thus, the compiler will steadfastly refuse to allow non-final
3434
/// classes to conform to the `Attachable` protocol. We could get around this
35-
/// by changing the signature of `withUnsafeBufferPointer()` so that the
35+
/// by changing the signature of `withUnsafeBytes()` so that the
3636
/// generic parameter to `Attachment` is not `Self`, but that would defeat
3737
/// much of the purpose of making `Attachment` generic in the first place.
3838
/// (And no, the language does not let us write `where T: Self` anywhere
@@ -132,7 +132,7 @@ extension _AttachableImageContainer: AttachableContainer {
132132
image
133133
}
134134

135-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
135+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
136136
let data = NSMutableData()
137137

138138
// Convert the image to a CGImage.

Sources/Overlays/_Testing_Foundation/Attachments/Attachable+Encodable+NSSecureCoding.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ public import Foundation
2121
@_spi(Experimental)
2222
extension Attachable where Self: Encodable & NSSecureCoding {
2323
@_documentation(visibility: private)
24-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
25-
try _Testing_Foundation.withUnsafeBufferPointer(encoding: self, for: attachment, body)
24+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
25+
try _Testing_Foundation.withUnsafeBytes(encoding: self, for: attachment, body)
2626
}
2727
}
2828
#endif

Sources/Overlays/_Testing_Foundation/Attachments/Attachable+Encodable.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
@_spi(Experimental) public import Testing
1313
private import Foundation
1414

15-
/// A common implementation of ``withUnsafeBufferPointer(for:_:)`` that is
16-
/// used when a type conforms to `Encodable`, whether or not it also conforms
17-
/// to `NSSecureCoding`.
15+
/// A common implementation of ``withUnsafeBytes(for:_:)`` that is used when a
16+
/// type conforms to `Encodable`, whether or not it also conforms to
17+
/// `NSSecureCoding`.
1818
///
1919
/// - Parameters:
2020
/// - attachableValue: The value to encode.
@@ -27,7 +27,7 @@ private import Foundation
2727
///
2828
/// - Throws: Whatever is thrown by `body`, or any error that prevented the
2929
/// creation of the buffer.
30-
func withUnsafeBufferPointer<E, R>(encoding attachableValue: borrowing E, for attachment: borrowing Attachment<E>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R where E: Attachable & Encodable {
30+
func withUnsafeBytes<E, R>(encoding attachableValue: borrowing E, for attachment: borrowing Attachment<E>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R where E: Attachable & Encodable {
3131
let format = try EncodingFormat(for: attachment)
3232

3333
let data: Data
@@ -86,8 +86,8 @@ extension Attachable where Self: Encodable {
8686
/// _and_ [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding),
8787
/// the default implementation of this function uses the value's conformance
8888
/// to `Encodable`.
89-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
90-
try _Testing_Foundation.withUnsafeBufferPointer(encoding: self, for: attachment, body)
89+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
90+
try _Testing_Foundation.withUnsafeBytes(encoding: self, for: attachment, body)
9191
}
9292
}
9393
#endif

Sources/Overlays/_Testing_Foundation/Attachments/Attachable+NSSecureCoding.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ extension Attachable where Self: NSSecureCoding {
4646
/// _and_ [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding),
4747
/// the default implementation of this function uses the value's conformance
4848
/// to `Encodable`.
49-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
49+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
5050
let format = try EncodingFormat(for: attachment)
5151

5252
var data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)

Sources/Overlays/_Testing_Foundation/Attachments/Data+Attachable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public import Foundation
1414

1515
@_spi(Experimental)
1616
extension Data: Attachable {
17-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
17+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
1818
try withUnsafeBytes(body)
1919
}
2020
}

Sources/Overlays/_Testing_Foundation/Attachments/_AttachableURLContainer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ extension _AttachableURLContainer: AttachableContainer {
3636
url
3737
}
3838

39-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
39+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
4040
try data.withUnsafeBytes(body)
4141
}
4242

Sources/Testing/Attachments/Attachable.swift

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
/// A protocol describing a type that can be attached to a test report or
1212
/// written to disk when a test is run.
1313
///
14-
/// To attach an attachable value to a test report or test run output, use it to
15-
/// initialize a new instance of ``Attachment``, then call
16-
/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached
17-
/// once.
14+
/// To attach an attachable value to a test, pass it to ``Attachment/record(_:named:sourceLocation:)``.
15+
/// To further configure an attachable value before you attach it, use it to
16+
/// initialize an instance of ``Attachment`` and set its properties before
17+
/// passing it to ``Attachment/record(_:sourceLocation:)``. An attachable
18+
/// value can only be attached to a test once.
1819
///
1920
/// The testing library provides default conformances to this protocol for a
2021
/// variety of standard library types. Most user-defined types do not need to
@@ -63,7 +64,7 @@ public protocol Attachable: ~Copyable {
6364
/// the buffer to contain an image in PNG format, JPEG format, etc., but it
6465
/// would not be idiomatic for the buffer to contain a textual description of
6566
/// the image.
66-
borrowing func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R
67+
borrowing func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R
6768

6869
/// Generate a preferred name for the given attachment.
6970
///
@@ -99,8 +100,8 @@ extension Attachable where Self: Collection, Element == UInt8 {
99100
count
100101
}
101102

102-
// We do not provide an implementation of withUnsafeBufferPointer(for:_:) here
103-
// because there is no way in the standard library to statically detect if a
103+
// We do not provide an implementation of withUnsafeBytes(for:_:) here because
104+
// there is no way in the standard library to statically detect if a
104105
// collection can provide contiguous storage (_HasContiguousBytes is not API.)
105106
// If withContiguousStorageIfAvailable(_:) fails, we don't want to make a
106107
// (potentially expensive!) copy of the collection.
@@ -120,28 +121,28 @@ extension Attachable where Self: StringProtocol {
120121
// developers can attach raw data when needed.
121122
@_spi(Experimental)
122123
extension Array<UInt8>: Attachable {
123-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
124+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
124125
try withUnsafeBytes(body)
125126
}
126127
}
127128

128129
@_spi(Experimental)
129130
extension ContiguousArray<UInt8>: Attachable {
130-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
131+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
131132
try withUnsafeBytes(body)
132133
}
133134
}
134135

135136
@_spi(Experimental)
136137
extension ArraySlice<UInt8>: Attachable {
137-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
138+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
138139
try withUnsafeBytes(body)
139140
}
140141
}
141142

142143
@_spi(Experimental)
143144
extension String: Attachable {
144-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
145+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
145146
var selfCopy = self
146147
return try selfCopy.withUTF8 { utf8 in
147148
try body(UnsafeRawBufferPointer(utf8))
@@ -151,7 +152,7 @@ extension String: Attachable {
151152

152153
@_spi(Experimental)
153154
extension Substring: Attachable {
154-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
155+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
155156
var selfCopy = self
156157
return try selfCopy.withUTF8 { utf8 in
157158
try body(UnsafeRawBufferPointer(utf8))

Sources/Testing/Attachments/AttachableContainer.swift

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,19 @@
1212
/// written to disk when a test is run and which contains another value that it
1313
/// stands in for.
1414
///
15-
/// To attach an attachable value to a test report or test run output, use it to
16-
/// initialize a new instance of ``Attachment``, then call
17-
/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached
18-
/// once.
15+
/// To attach an attachable value to a test, pass it to ``Attachment/record(_:named:sourceLocation:)``.
16+
/// To further configure an attachable value before you attach it, use it to
17+
/// initialize an instance of ``Attachment`` and set its properties before
18+
/// passing it to ``Attachment/record(_:sourceLocation:)``. An attachable
19+
/// value can only be attached to a test once.
1920
///
2021
/// A type can conform to this protocol if it represents another type that
2122
/// cannot directly conform to ``Attachable``, such as a non-final class or a
2223
/// type declared in a third-party module.
2324
@_spi(Experimental)
2425
public protocol AttachableContainer<AttachableValue>: Attachable, ~Copyable {
25-
#if hasFeature(SuppressedAssociatedTypes)
26-
/// The type of the attachable value represented by this type.
27-
associatedtype AttachableValue: ~Copyable
28-
#else
2926
/// The type of the attachable value represented by this type.
3027
associatedtype AttachableValue
31-
#endif
3228

3329
/// The attachable value represented by this instance.
3430
var attachableValue: AttachableValue { get }

Sources/Testing/Attachments/Attachment.swift

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,15 @@ public struct AnyAttachable: AttachableContainer, Copyable, Sendable {
143143
attachableValue.estimatedAttachmentByteCount
144144
}
145145

146-
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
146+
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
147147
func open<T>(_ attachableValue: T, for attachment: borrowing Attachment<Self>) throws -> R where T: Attachable & Sendable & Copyable {
148148
let temporaryAttachment = Attachment<T>(
149149
_attachableValue: attachableValue,
150150
fileSystemPath: attachment.fileSystemPath,
151151
_preferredName: attachment._preferredName,
152152
sourceLocation: attachment.sourceLocation
153153
)
154-
return try temporaryAttachment.withUnsafeBufferPointer(body)
154+
return try temporaryAttachment.withUnsafeBytes(body)
155155
}
156156
return try open(attachableValue, for: attachment)
157157
}
@@ -220,25 +220,61 @@ extension Attachment where AttachableValue: AttachableContainer & ~Copyable {
220220

221221
#if !SWT_NO_LAZY_ATTACHMENTS
222222
extension Attachment where AttachableValue: Sendable & Copyable {
223-
/// Attach this instance to the current test.
223+
/// Attach an attachment to the current test.
224224
///
225225
/// - Parameters:
226+
/// - attachment: The attachment to attach.
226227
/// - sourceLocation: The source location of the call to this function.
227228
///
229+
/// When attaching a value of a type that does not conform to both
230+
/// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
231+
/// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
232+
/// the testing library encodes it as data immediately. If the value cannot be
233+
/// encoded and an error is thrown, that error is recorded as an issue in the
234+
/// current test and the attachment is not written to the test report or to
235+
/// disk.
236+
///
228237
/// An attachment can only be attached once.
229238
@_documentation(visibility: private)
230-
public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) {
231-
var attachmentCopy = Attachment<AnyAttachable>(self)
239+
public static func record(_ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) {
240+
var attachmentCopy = Attachment<AnyAttachable>(attachment)
232241
attachmentCopy.sourceLocation = sourceLocation
233242
Event.post(.valueAttached(attachmentCopy))
234243
}
244+
245+
/// Attach a value to the current test.
246+
///
247+
/// - Parameters:
248+
/// - attachableValue: The value to attach.
249+
/// - preferredName: The preferred name of the attachment when writing it to
250+
/// a test report or to disk. If `nil`, the testing library attempts to
251+
/// derive a reasonable filename for the attached value.
252+
/// - sourceLocation: The source location of the call to this function.
253+
///
254+
/// When attaching a value of a type that does not conform to both
255+
/// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
256+
/// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
257+
/// the testing library encodes it as data immediately. If the value cannot be
258+
/// encoded and an error is thrown, that error is recorded as an issue in the
259+
/// current test and the attachment is not written to the test report or to
260+
/// disk.
261+
///
262+
/// This function creates a new instance of ``Attachment`` and immediately
263+
/// attaches it to the current test.
264+
///
265+
/// An attachment can only be attached once.
266+
@_documentation(visibility: private)
267+
public static func record(_ attachableValue: consuming AttachableValue, named preferredName: String? = nil, sourceLocation: SourceLocation = #_sourceLocation) {
268+
record(Self(attachableValue, named: preferredName), sourceLocation: sourceLocation)
269+
}
235270
}
236271
#endif
237272

238273
extension Attachment where AttachableValue: ~Copyable {
239-
/// Attach this instance to the current test.
274+
/// Attach an attachment to the current test.
240275
///
241276
/// - Parameters:
277+
/// - attachment: The attachment to attach.
242278
/// - sourceLocation: The source location of the call to this function.
243279
///
244280
/// When attaching a value of a type that does not conform to both
@@ -250,14 +286,14 @@ extension Attachment where AttachableValue: ~Copyable {
250286
/// disk.
251287
///
252288
/// An attachment can only be attached once.
253-
public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) {
289+
public static func record(_ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) {
254290
do {
255-
let attachmentCopy = try withUnsafeBufferPointer { buffer in
291+
let attachmentCopy = try attachment.withUnsafeBytes { buffer in
256292
let attachableContainer = AnyAttachable(attachableValue: Array(buffer))
257293
return Attachment<AnyAttachable>(
258294
_attachableValue: attachableContainer,
259-
fileSystemPath: fileSystemPath,
260-
_preferredName: preferredName, // invokes preferredName(for:basedOn:)
295+
fileSystemPath: attachment.fileSystemPath,
296+
_preferredName: attachment.preferredName, // invokes preferredName(for:basedOn:)
261297
sourceLocation: sourceLocation
262298
)
263299
}
@@ -267,6 +303,31 @@ extension Attachment where AttachableValue: ~Copyable {
267303
Issue(kind: .valueAttachmentFailed(error), comments: [], sourceContext: sourceContext).record()
268304
}
269305
}
306+
307+
/// Attach a value to the current test.
308+
///
309+
/// - Parameters:
310+
/// - attachableValue: The value to attach.
311+
/// - preferredName: The preferred name of the attachment when writing it to
312+
/// a test report or to disk. If `nil`, the testing library attempts to
313+
/// derive a reasonable filename for the attached value.
314+
/// - sourceLocation: The source location of the call to this function.
315+
///
316+
/// When attaching a value of a type that does not conform to both
317+
/// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
318+
/// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
319+
/// the testing library encodes it as data immediately. If the value cannot be
320+
/// encoded and an error is thrown, that error is recorded as an issue in the
321+
/// current test and the attachment is not written to the test report or to
322+
/// disk.
323+
///
324+
/// This function creates a new instance of ``Attachment`` and immediately
325+
/// attaches it to the current test.
326+
///
327+
/// An attachment can only be attached once.
328+
public static func record(_ attachableValue: consuming AttachableValue, named preferredName: String? = nil, sourceLocation: SourceLocation = #_sourceLocation) {
329+
record(Self(attachableValue, named: preferredName), sourceLocation: sourceLocation)
330+
}
270331
}
271332

272333
// MARK: - Getting the serialized form of an attachable value (generically)
@@ -286,10 +347,10 @@ extension Attachment where AttachableValue: ~Copyable {
286347
///
287348
/// The testing library uses this function when writing an attachment to a
288349
/// test report or to a file on disk. This function calls the
289-
/// ``Attachable/withUnsafeBufferPointer(for:_:)`` function on this
290-
/// attachment's ``attachableValue-2tnj5`` property.
291-
@inlinable public borrowing func withUnsafeBufferPointer<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
292-
try attachableValue.withUnsafeBufferPointer(for: self, body)
350+
/// ``Attachable/withUnsafeBytes(for:_:)`` function on this attachment's
351+
/// ``attachableValue-2tnj5`` property.
352+
@inlinable public borrowing func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
353+
try attachableValue.withUnsafeBytes(for: self, body)
293354
}
294355
}
295356

@@ -391,7 +452,7 @@ extension Attachment where AttachableValue: ~Copyable {
391452

392453
// There should be no code path that leads to this call where the attachable
393454
// value is nil.
394-
try withUnsafeBufferPointer { buffer in
455+
try withUnsafeBytes { buffer in
395456
try file!.write(buffer)
396457
}
397458

0 commit comments

Comments
 (0)