Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ extension Array where Element == PackageDescription.SwiftSetting {

result += [
.enableUpcomingFeature("ExistentialAny"),
.enableExperimentalFeature("SuppressedAssociatedTypes"),

.enableExperimentalFeature("AccessLevelOnImport"),
.enableUpcomingFeature("InternalImportsByDefault"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import UniformTypeIdentifiers
/// such a requirement, and all image types we care about are non-final
/// classes. Thus, the compiler will steadfastly refuse to allow non-final
/// classes to conform to the `Attachable` protocol. We could get around this
/// by changing the signature of `withUnsafeBufferPointer()` so that the
/// by changing the signature of `withUnsafeBytes()` so that the
/// generic parameter to `Attachment` is not `Self`, but that would defeat
/// much of the purpose of making `Attachment` generic in the first place.
/// (And no, the language does not let us write `where T: Self` anywhere
Expand Down Expand Up @@ -132,7 +132,7 @@ extension _AttachableImageContainer: AttachableContainer {
image
}

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

// Convert the image to a CGImage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public import Foundation
@_spi(Experimental)
extension Attachable where Self: Encodable & NSSecureCoding {
@_documentation(visibility: private)
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try _Testing_Foundation.withUnsafeBufferPointer(encoding: self, for: attachment, body)
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try _Testing_Foundation.withUnsafeBytes(encoding: self, for: attachment, body)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
@_spi(Experimental) public import Testing
private import Foundation

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

let data: Data
Expand Down Expand Up @@ -86,8 +86,8 @@ extension Attachable where Self: Encodable {
/// _and_ [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding),
/// the default implementation of this function uses the value's conformance
/// to `Encodable`.
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try _Testing_Foundation.withUnsafeBufferPointer(encoding: self, for: attachment, body)
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try _Testing_Foundation.withUnsafeBytes(encoding: self, for: attachment, body)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ extension Attachable where Self: NSSecureCoding {
/// _and_ [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding),
/// the default implementation of this function uses the value's conformance
/// to `Encodable`.
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
let format = try EncodingFormat(for: attachment)

var data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public import Foundation

@_spi(Experimental)
extension Data: Attachable {
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try withUnsafeBytes(body)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ extension _AttachableURLContainer: AttachableContainer {
url
}

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

Expand Down
25 changes: 13 additions & 12 deletions Sources/Testing/Attachments/Attachable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
/// A protocol describing a type that can be attached to a test report or
/// written to disk when a test is run.
///
/// To attach an attachable value to a test report or test run output, use it to
/// initialize a new instance of ``Attachment``, then call
/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached
/// once.
/// To attach an attachable value to a test, pass it to ``Attachment/record(_:named:sourceLocation:)``.
/// To further configure an attachable value before you attach it, use it to
/// initialize an instance of ``Attachment`` and set its properties before
/// passing it to ``Attachment/record(_:sourceLocation:)``. An attachable
/// value can only be attached to a test once.
///
/// The testing library provides default conformances to this protocol for a
/// variety of standard library types. Most user-defined types do not need to
Expand Down Expand Up @@ -63,7 +64,7 @@ public protocol Attachable: ~Copyable {
/// the buffer to contain an image in PNG format, JPEG format, etc., but it
/// would not be idiomatic for the buffer to contain a textual description of
/// the image.
borrowing func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R
borrowing func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R

/// Generate a preferred name for the given attachment.
///
Expand Down Expand Up @@ -99,8 +100,8 @@ extension Attachable where Self: Collection, Element == UInt8 {
count
}

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

@_spi(Experimental)
extension ContiguousArray<UInt8>: Attachable {
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try withUnsafeBytes(body)
}
}

@_spi(Experimental)
extension ArraySlice<UInt8>: Attachable {
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try withUnsafeBytes(body)
}
}

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

@_spi(Experimental)
extension Substring: Attachable {
public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
var selfCopy = self
return try selfCopy.withUTF8 { utf8 in
try body(UnsafeRawBufferPointer(utf8))
Expand Down
14 changes: 5 additions & 9 deletions Sources/Testing/Attachments/AttachableContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,19 @@
/// written to disk when a test is run and which contains another value that it
/// stands in for.
///
/// To attach an attachable value to a test report or test run output, use it to
/// initialize a new instance of ``Attachment``, then call
/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached
/// once.
/// To attach an attachable value to a test, pass it to ``Attachment/record(_:named:sourceLocation:)``.
/// To further configure an attachable value before you attach it, use it to
/// initialize an instance of ``Attachment`` and set its properties before
/// passing it to ``Attachment/record(_:sourceLocation:)``. An attachable
/// value can only be attached to a test once.
///
/// A type can conform to this protocol if it represents another type that
/// cannot directly conform to ``Attachable``, such as a non-final class or a
/// type declared in a third-party module.
@_spi(Experimental)
public protocol AttachableContainer<AttachableValue>: Attachable, ~Copyable {
#if hasFeature(SuppressedAssociatedTypes)
/// The type of the attachable value represented by this type.
associatedtype AttachableValue: ~Copyable
#else
/// The type of the attachable value represented by this type.
associatedtype AttachableValue
#endif

/// The attachable value represented by this instance.
var attachableValue: AttachableValue { get }
Expand Down
91 changes: 76 additions & 15 deletions Sources/Testing/Attachments/Attachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,15 @@ public struct AnyAttachable: AttachableContainer, Copyable, Sendable {
attachableValue.estimatedAttachmentByteCount
}

public func withUnsafeBufferPointer<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
func open<T>(_ attachableValue: T, for attachment: borrowing Attachment<Self>) throws -> R where T: Attachable & Sendable & Copyable {
let temporaryAttachment = Attachment<T>(
_attachableValue: attachableValue,
fileSystemPath: attachment.fileSystemPath,
_preferredName: attachment._preferredName,
sourceLocation: attachment.sourceLocation
)
return try temporaryAttachment.withUnsafeBufferPointer(body)
return try temporaryAttachment.withUnsafeBytes(body)
}
return try open(attachableValue, for: attachment)
}
Expand Down Expand Up @@ -220,25 +220,61 @@ extension Attachment where AttachableValue: AttachableContainer & ~Copyable {

#if !SWT_NO_LAZY_ATTACHMENTS
extension Attachment where AttachableValue: Sendable & Copyable {
/// Attach this instance to the current test.
/// Attach an attachment to the current test.
///
/// - Parameters:
/// - attachment: The attachment to attach.
/// - sourceLocation: The source location of the call to this function.
///
/// When attaching a value of a type that does not conform to both
/// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
/// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
/// the testing library encodes it as data immediately. If the value cannot be
/// encoded and an error is thrown, that error is recorded as an issue in the
/// current test and the attachment is not written to the test report or to
/// disk.
///
/// An attachment can only be attached once.
@_documentation(visibility: private)
public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) {
var attachmentCopy = Attachment<AnyAttachable>(self)
public static func record(_ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) {
var attachmentCopy = Attachment<AnyAttachable>(attachment)
attachmentCopy.sourceLocation = sourceLocation
Event.post(.valueAttached(attachmentCopy))
}

/// Attach a value to the current test.
///
/// - Parameters:
/// - attachableValue: The value to attach.
/// - preferredName: The preferred name of the attachment when writing it to
/// a test report or to disk. If `nil`, the testing library attempts to
/// derive a reasonable filename for the attached value.
/// - sourceLocation: The source location of the call to this function.
///
/// When attaching a value of a type that does not conform to both
/// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
/// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
/// the testing library encodes it as data immediately. If the value cannot be
/// encoded and an error is thrown, that error is recorded as an issue in the
/// current test and the attachment is not written to the test report or to
/// disk.
///
/// This function creates a new instance of ``Attachment`` and immediately
/// attaches it to the current test.
///
/// An attachment can only be attached once.
@_documentation(visibility: private)
public static func record(_ attachableValue: consuming AttachableValue, named preferredName: String? = nil, sourceLocation: SourceLocation = #_sourceLocation) {
record(Self(attachableValue, named: preferredName), sourceLocation: sourceLocation)
}
}
#endif

extension Attachment where AttachableValue: ~Copyable {
/// Attach this instance to the current test.
/// Attach an attachment to the current test.
///
/// - Parameters:
/// - attachment: The attachment to attach.
/// - sourceLocation: The source location of the call to this function.
///
/// When attaching a value of a type that does not conform to both
Expand All @@ -250,14 +286,14 @@ extension Attachment where AttachableValue: ~Copyable {
/// disk.
///
/// An attachment can only be attached once.
public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) {
public static func record(_ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) {
do {
let attachmentCopy = try withUnsafeBufferPointer { buffer in
let attachmentCopy = try attachment.withUnsafeBytes { buffer in
let attachableContainer = AnyAttachable(attachableValue: Array(buffer))
return Attachment<AnyAttachable>(
_attachableValue: attachableContainer,
fileSystemPath: fileSystemPath,
_preferredName: preferredName, // invokes preferredName(for:basedOn:)
fileSystemPath: attachment.fileSystemPath,
_preferredName: attachment.preferredName, // invokes preferredName(for:basedOn:)
sourceLocation: sourceLocation
)
}
Expand All @@ -267,6 +303,31 @@ extension Attachment where AttachableValue: ~Copyable {
Issue(kind: .valueAttachmentFailed(error), comments: [], sourceContext: sourceContext).record()
}
}

/// Attach a value to the current test.
///
/// - Parameters:
/// - attachableValue: The value to attach.
/// - preferredName: The preferred name of the attachment when writing it to
/// a test report or to disk. If `nil`, the testing library attempts to
/// derive a reasonable filename for the attached value.
/// - sourceLocation: The source location of the call to this function.
///
/// When attaching a value of a type that does not conform to both
/// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and
/// [`Copyable`](https://developer.apple.com/documentation/swift/copyable),
/// the testing library encodes it as data immediately. If the value cannot be
/// encoded and an error is thrown, that error is recorded as an issue in the
/// current test and the attachment is not written to the test report or to
/// disk.
///
/// This function creates a new instance of ``Attachment`` and immediately
/// attaches it to the current test.
///
/// An attachment can only be attached once.
public static func record(_ attachableValue: consuming AttachableValue, named preferredName: String? = nil, sourceLocation: SourceLocation = #_sourceLocation) {
record(Self(attachableValue, named: preferredName), sourceLocation: sourceLocation)
}
}

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

Expand Down Expand Up @@ -391,7 +452,7 @@ extension Attachment where AttachableValue: ~Copyable {

// There should be no code path that leads to this call where the attachable
// value is nil.
try withUnsafeBufferPointer { buffer in
try withUnsafeBytes { buffer in
try file!.write(buffer)
}

Expand Down
Loading