Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promote attachments to API #973

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 6 additions & 1 deletion Documentation/ABI/JSON.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,19 +188,24 @@ sufficient information to display the event in a human-readable format.
"kind": <event-kind>,
"instant": <instant>, ; when the event occurred
["issue": <issue>,] ; the recorded issue (if "kind" is "issueRecorded")
["attachment": <attachment>,] ; the attachment (if kind is "valueAttached")
"messages": <array:message>,
["testID": <test-id>,]
}

<event-kind> ::= "runStarted" | "testStarted" | "testCaseStarted" |
"issueRecorded" | "testCaseEnded" | "testEnded" | "testSkipped" |
"runEnded" ; additional event kinds may be added in the future
"runEnded" | "valueAttached"; additional event kinds may be added in the future

<issue> ::= {
"isKnown": <bool>, ; is this a known issue or not?
["sourceLocation": <source-location>,] ; where the issue occurred, if known
}

<attachment> ::= {
"path": <string>, ; the absolute path to the attachment on disk
}

<message> ::= {
"symbol": <message-symbol>,
"text": <string>, ; the human-readable text of this message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//

#if SWT_TARGET_OS_APPLE && canImport(CoreGraphics)
@_spi(Experimental) public import Testing
public import Testing
private import CoreGraphics

private import ImageIO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//

#if canImport(Foundation)
@_spi(Experimental) public import Testing
public import Testing
public import Foundation

// This implementation is necessary to let the compiler disambiguate when a type
Expand All @@ -18,7 +18,9 @@ public import Foundation
// (which explicitly document what happens when a type conforms to both
// protocols.)

@_spi(Experimental)
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
extension Attachable where Self: Encodable & NSSecureCoding {
@_documentation(visibility: private)
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//

#if canImport(Foundation)
@_spi(Experimental) public import Testing
public import Testing
private import Foundation

/// A common implementation of ``withUnsafeBytes(for:_:)`` that is used when a
Expand Down Expand Up @@ -53,7 +53,10 @@ func withUnsafeBytes<E, R>(encoding attachableValue: borrowing E, for attachment
// Implement the protocol requirements generically for any encodable value by
// encoding to JSON. This lets developers provide trivial conformance to the
// protocol for types that already support Codable.
@_spi(Experimental)

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
extension Attachable where Self: Encodable {
/// Encode this value into a buffer using either [`PropertyListEncoder`](https://developer.apple.com/documentation/foundation/propertylistencoder)
/// or [`JSONEncoder`](https://developer.apple.com/documentation/foundation/jsonencoder),
Expand Down Expand Up @@ -86,6 +89,10 @@ 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`.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try _Testing_Foundation.withUnsafeBytes(encoding: self, for: attachment, body)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
//

#if canImport(Foundation)
@_spi(Experimental) public import Testing
public import Testing
public import Foundation

// As with Encodable, implement the protocol requirements for
// NSSecureCoding-conformant classes by default. The implementation uses
// NSKeyedArchiver for encoding.
@_spi(Experimental)

/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
extension Attachable where Self: NSSecureCoding {
/// Encode this object using [`NSKeyedArchiver`](https://developer.apple.com/documentation/foundation/nskeyedarchiver)
/// into a buffer, then call a function and pass that buffer to it.
Expand Down Expand Up @@ -46,6 +49,10 @@ 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`.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
let format = try EncodingFormat(for: attachment)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//

#if canImport(Foundation)
@_spi(Experimental) public import Testing
public import Testing
public import Foundation

#if !SWT_NO_PROCESS_SPAWNING && os(Windows)
Expand All @@ -32,7 +32,6 @@ extension URL {
}
}

@_spi(Experimental)
extension Attachment where AttachableValue == _AttachableURLContainer {
#if SWT_TARGET_OS_APPLE
/// An operation queue to use for asynchronously reading data from disk.
Expand All @@ -51,6 +50,10 @@ extension Attachment where AttachableValue == _AttachableURLContainer {
/// attachment.
///
/// - Throws: Any error that occurs attempting to read from `url`.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public init(
contentsOf url: URL,
named preferredName: String? = nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
//

#if canImport(Foundation)
@_spi(Experimental) public import Testing
public import Testing
public import Foundation

@_spi(Experimental)
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
extension Data: Attachable {
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
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 @@ -9,7 +9,7 @@
//

#if canImport(Foundation)
@_spi(Experimental) import Testing
import Testing
import Foundation

/// An enumeration describing the encoding formats we support for `Encodable`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
//

#if canImport(Foundation)
@_spi(Experimental) public import Testing
public import Testing
public import Foundation

/// A wrapper type representing file system objects and URLs that can be
/// attached indirectly.
///
/// You do not need to use this type directly. Instead, initialize an instance
/// of ``Attachment`` using a file URL.
@_spi(Experimental)
public struct _AttachableURLContainer: Sendable {
/// The underlying URL.
var url: URL
Expand Down
8 changes: 3 additions & 5 deletions Sources/Testing/ABI/Encoded/ABI.EncodedEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension ABI {
case testStarted
case testCaseStarted
case issueRecorded
case valueAttached = "_valueAttached"
case valueAttached
case testCaseEnded
case testEnded
case testSkipped
Expand All @@ -50,9 +50,7 @@ extension ABI {
///
/// The value of this property is `nil` unless the value of the
/// ``kind-swift.property`` property is ``Kind-swift.enum/valueAttached``.
///
/// - Warning: Attachments are not yet part of the JSON schema.
var _attachment: EncodedAttachment<V>?
var attachment: EncodedAttachment<V>?

/// Human-readable messages associated with this event that can be presented
/// to the user.
Expand Down Expand Up @@ -82,7 +80,7 @@ extension ABI {
issue = EncodedIssue(encoding: recordedIssue, in: eventContext)
case let .valueAttached(attachment):
kind = .valueAttached
_attachment = EncodedAttachment(encoding: attachment, in: eventContext)
self.attachment = EncodedAttachment(encoding: attachment, in: eventContext)
case .testCaseEnded:
if eventContext.test?.isParameterized == false {
return nil
Expand Down
22 changes: 16 additions & 6 deletions Sources/Testing/Attachments/Attachable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
/// type cannot conform directly to this protocol (such as a non-final class or
/// a type declared in a third-party module), you can create a container type
/// that conforms to ``AttachableContainer`` to act as a proxy.
@_spi(Experimental)
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public protocol Attachable: ~Copyable {
/// An estimate of the number of bytes of memory needed to store this value as
/// an attachment.
Expand All @@ -42,6 +45,10 @@ public protocol Attachable: ~Copyable {
///
/// - Complexity: O(1) unless `Self` conforms to `Collection`, in which case
/// up to O(_n_) where _n_ is the length of the collection.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
var estimatedAttachmentByteCount: Int? { get }

/// Call a function and pass a buffer representing this instance to it.
Expand All @@ -64,6 +71,10 @@ 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.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
borrowing func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R

/// Generate a preferred name for the given attachment.
Expand All @@ -80,6 +91,10 @@ public protocol Attachable: ~Copyable {
/// when adding `attachment` to a test report or persisting it to storage. The
/// default implementation of this function returns `suggestedName` without
/// any changes.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
borrowing func preferredName(for attachment: borrowing Attachment<Self>, basedOn suggestedName: String) -> String
}

Expand Down Expand Up @@ -119,28 +134,24 @@ extension Attachable where Self: StringProtocol {

// Implement the protocol requirements for byte arrays and buffers so that
// developers can attach raw data when needed.
@_spi(Experimental)
extension Array<UInt8>: Attachable {
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 withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try withUnsafeBytes(body)
}
}

@_spi(Experimental)
extension ArraySlice<UInt8>: Attachable {
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 withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
var selfCopy = self
Expand All @@ -150,7 +161,6 @@ extension String: Attachable {
}
}

@_spi(Experimental)
extension Substring: Attachable {
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
var selfCopy = self
Expand Down
13 changes: 12 additions & 1 deletion Sources/Testing/Attachments/AttachableContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,22 @@
/// 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)
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
public protocol AttachableContainer<AttachableValue>: Attachable, ~Copyable {
/// The type of the attachable value represented by this type.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
associatedtype AttachableValue

/// The attachable value represented by this instance.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// }
var attachableValue: AttachableValue { get }
}
Loading