Skip to content
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
13 changes: 0 additions & 13 deletions Sources/Testing/Attachments/Attachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,6 @@ private import _TestingInternals
/// record the attachment, call ``Attachment/record(_:sourceLocation:)``.
/// Alternatively, pass your attachable value directly to ``Attachment/record(_:named:sourceLocation:)``.
///
/// By default, the testing library saves your attachments as soon as you call
/// ``Attachment/record(_:sourceLocation:)`` or
/// ``Attachment/record(_:named:sourceLocation:)``. You can access saved
/// attachments after your tests finish running:
///
/// - When using Xcode, you can access attachments from the test report.
/// - When using Visual Studio Code, the testing library saves attachments to
/// `.build/attachments` by default. Visual Studio Code reports the paths to
/// individual attachments in its Tests Results panel.
/// - When using Swift Package Manager's `swift test` command, you can pass the
/// `--attachments-path` option. The testing library saves attachments to the
/// specified directory.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.2)
/// @Available(Xcode, introduced: 26.0)
Expand Down
9 changes: 0 additions & 9 deletions Sources/Testing/Attachments/Images/AttachableAsImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,6 @@
/// Instead, the testing library provides additional initializers on [`Attachment`](https://developer.apple.com/documentation/testing/attachment)
/// that take instances of such types and handle converting them to image data when needed.
///
/// You can attach instances of the following system-provided image types to a
/// test:
///
/// | Platform | Supported Types |
/// |-|-|
/// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) |
/// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
/// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) |
///
/// You do not generally need to add your own conformances to this protocol. If
/// you have an image in another format that needs to be attached to a test,
/// first convert it to an instance of one of the types above.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
/// instance of this type, the testing library infers which format to use based
/// on the attachment's preferred name.
///
/// The PNG and JPEG image formats are always supported. The set of additional
/// supported image formats is platform-specific:
/// The testing library always supports the PNG and JPEG image formats. The set
/// of additional supported image formats is platform-specific:
///
/// - On Apple platforms, you can use [`CGImageDestinationCopyTypeIdentifiers()`](https://developer.apple.com/documentation/imageio/cgimagedestinationcopytypeidentifiers())
/// from the [Image I/O framework](https://developer.apple.com/documentation/imageio)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,6 @@ extension Attachment {
/// This value is used when recording issues associated with the
/// attachment.
///
/// You can attach instances of the following system-provided image types to a
/// test:
///
/// | Platform | Supported Types |
/// |-|-|
/// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) |
/// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
/// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) |
///
/// The testing library uses the image format specified by `imageFormat`. Pass
/// `nil` to let the testing library decide which image format to use. If you
/// pass `nil`, then the image format that the testing library uses depends on
Expand Down Expand Up @@ -70,22 +61,14 @@ extension Attachment {
/// - sourceLocation: The source location of the call to this function.
///
/// This function creates a new instance of ``Attachment`` wrapping `image`
/// and immediately attaches it to the current test. You can attach instances
/// of the following system-provided image types to a test:
///
/// | Platform | Supported Types |
/// |-|-|
/// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) |
/// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
/// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) |
///
/// The testing library uses the image format specified by `imageFormat`. Pass
/// `nil` to let the testing library decide which image format to use. If you
/// pass `nil`, then the image format that the testing library uses depends on
/// the path extension you specify in `preferredName`, if any. If you do not
/// specify a path extension, or if the path extension you specify doesn't
/// correspond to an image format the operating system knows how to write, the
/// testing library selects an appropriate image format for you.
/// and immediately attaches it to the current test. The testing library uses
/// the image format specified by `imageFormat`. Pass `nil` to let the testing
/// library decide which image format to use. If you pass `nil`, then the
/// image format that the testing library uses depends on the path extension
/// you specify in `preferredName`, if any. If you do not specify a path
/// extension, or if the path extension you specify doesn't correspond to an
/// image format the operating system knows how to write, the testing library
/// selects an appropriate image format for you.
///
/// @Metadata {
/// @Available(Swift, introduced: 6.3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@
//

/// A wrapper type for images that can be indirectly attached to a test.
///
/// You can attach instances of the following system-provided image types to a
/// test:
///
/// | Platform | Supported Types |
/// |-|-|
/// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) |
/// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
/// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) |
#if SWT_NO_IMAGE_ATTACHMENTS
@_unavailableInEmbedded
@available(*, unavailable, message: "Image attachments are not available on this platform.")
Expand Down
169 changes: 169 additions & 0 deletions Sources/Testing/Testing.docc/Attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,175 @@ Attach values to tests to help diagnose issues and gather feedback.
Attach values such as strings and files to tests. Implement the ``Attachable``
protocol to create your own attachable types.

### Attach data or strings

If your test produces encoded data that you want to save as an attachment, you
can call ``Attachment/record(_:named:sourceLocation:)``:

```swift
struct SalesReport { ... }

@Test func `sales report adds up`() async throws {
let salesReport = await generateSalesReport()
try salesReport.validate()
let bytes: [UInt8] = try salesReport.convertToCSV()
Attachment.record(bytes, named: "sales report.csv")
}
```

You can attach an instance of [`Array<UInt8>`](https://developer.apple.com/documentation/swift/array),
[`ContiguousArray<UInt8>`](https://developer.apple.com/documentation/swift/contiguousarray),
[`ArraySlice<UInt8>`](https://developer.apple.com/documentation/swift/arrayslice),
or [`Data`](https://developer.apple.com/documentation/foundation/data) as
these types automatically conform to ``Attachable``.

You can also attach an instance of [`String`](https://developer.apple.com/documentation/swift/string)
or [`Substring`](https://developer.apple.com/documentation/swift/substring). The
testing library assumes that it should treat attached strings as UTF-8 text
files. If you want to save a string as an attachment using a different encoding,
convert it to [`Data`](https://developer.apple.com/documentation/foundation/data)
using [`data(using:allowLossyConversion:)`](https://developer.apple.com/documentation/swift/stringprotocol/data(using:allowlossyconversion:))
and attach the resulting data instead of the original string.

### Attach encodable values

If you have a value you want to save as an attachment that conforms to either
[`Encodable`](https://developer.apple.com/documentation/swift/encodable) or
[`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding),
you can extend it to add conformance to ``Attachable``. When you import the
[Foundation](https://developer.apple.com/documentation/foundation) module, the
testing library automatically provides a default implementation of
``Attachable`` to types that also conform to [`Encodable`](https://developer.apple.com/documentation/swift/encodable)
or [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding):

```swift
import Testing
import Foundation

struct SalesReport { ... }
extension SalesReport: Encodable, Attachable {}

@Test func `sales report adds up`() async throws {
let salesReport = await generateSalesReport()
try salesReport.validate()
Attachment.record(salesReport, named: "sales report.json")
}
```

- Important: The testing library only provides these default implementations if
your test target imports the [Foundation](https://developer.apple.com/documentation/foundation)
module.

### Attach images

You can attach instances of the following system-provided image types to a test:

| Platform | Supported Types |
|-|-|
| macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) |
| iOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
| watchOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
| Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) |

When you attach an image to a test, you can specify the image format to use in
addition to a preferred name:

```swift
struct SalesReport { ... }

@Test func `sales report adds up`() async throws {
let salesReport = await generateSalesReport()
let image = try salesReport.renderTrendsGraph()
Attachment.record(image, named: "sales report", as: .png)
}
```

If you don't specify an image format when attaching an image to a test, the
testing library infers which format to use based on the preferred name you pass.

### Attach other values

If you have a value that needs a custom encoded representation when you save it
as an attachment, implement ``Attachable/withUnsafeBytes(for:_:)``. The
implementation of this function calls its `body` argument and passes the encoded
representation of `self` or, if a failure occurs, throws an error representing
that failure:

```swift
struct SalesReport { ... }

extension SalesReport: Attachable {
borrowing func withUnsafeBytes<R>(
for attachment: borrowing Attachment<Self>,
_ body: (UnsafeRawBufferPointer) throws -> R
) throws -> R {
let bytes = try salesReport.convertToCSV() // might fail to convert to CSV
try bytes.withUnsafeBytes { buffer in // rethrows any error from `body`
try body(buffer)
}
}
}
```

If your type conforms to [`Sendable`](https://developer.apple.com/documentation/swift/sendable),
the testing library avoids calling this function until it needs to save the
attachment. If your type does _not_ conform to [`Sendable`](https://developer.apple.com/documentation/swift/sendable),
the testing library calls this function as soon as you record the attachment.

#### Customize attachment behavior

If you can reliably estimate in advance how large the encoded representation
will be, implement ``Attachable/estimatedAttachmentByteCount``. The testing
library uses the value of this property as a hint to optimize memory and disk
usage:

```swift
extension SalesReport: Attachable {
...

var estimatedAttachmentByteCount: Int? {
return self.entries.count * 123
}
}
```

You can also implement ``Attachable/preferredName(for:basedOn:)`` if you wish to
customize the name of the attachment when it is saved:

```swift
extension SalesReport: Attachable {
...

borrowing func preferredName(
for attachment: borrowing Attachment<Self>,
basedOn suggestedName: String
) -> String {
if suggestedName.lastIndex(of: ".") != nil {
// The name already contains a path extension, so do not append another.
return suggestedName
}

// Append ".csv" to the name so the resulting file opens as a spreadsheet.
return "\(suggestedName).csv"
}
}
```

### Inspect attachments after a test run ends

By default, the testing library saves your attachments as soon as you call
``Attachment/record(_:sourceLocation:)`` or
``Attachment/record(_:named:sourceLocation:)``. You can access saved attachments
after your tests finish running:

- When using Xcode, you can access attachments from the test report.
- When using Visual Studio Code, the testing library saves attachments to
`.build/attachments` by default. Visual Studio Code reports the paths to
individual attachments in its Tests Results panel.
- When using Swift Package Manager's `swift test` command, you can pass the
`--attachments-path` option. The testing library saves attachments to the
specified directory.
Comment on lines +185 to +187
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we clarify that if this CLI flag is not passed, attachments will not be saved anywhere? I wonder if that's even worth an "Important" callout or similar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave it as-is and let our DevPubs colleagues consider how to phrase it.


## Topics

### Attaching values to tests
Expand Down
Loading