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 that `imageFormat` specifies. Pass `nil` to let the testing
/// library select which image format to use. If you pass `nil`, the
/// image format that the testing library uses depends on the path extension
/// you specify in `preferredName`, if any. If you don't 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
173 changes: 173 additions & 0 deletions Sources/Testing/Testing.docc/Attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,179 @@ 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:)``:

Choose a reason for hiding this comment

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

Suggested change
can call ``Attachment/record(_:named:sourceLocation:)``:
can call ``Attachment/record(_:named:sourceLocation:)``.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using a colon here to introduce a code sample/snippet is pretty standard practice. Compare this (previously-reviewed) article: https://developer.apple.com/documentation/testing/enablinganddisabling

Since we're already doing it in other files, I'd prefer to remain consistent, but could I ask you to get a DevPubs verdict and, if changes are needed, we can apply them globally in another PR?

Copy link

@jgmcnutt jgmcnutt Nov 17, 2025

Choose a reason for hiding this comment

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

DevPubs verdict: We use the colon when we mention the snippet, such as "The following code example shows..." and "In the code below, X does Z....". If we don't mention the code, it isn't an introductory sentence and takes a period.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it sufficient to end the sentence(s) I have now and then add "For example:"? I do want to make sure it's clear that the following code is related to the preceding text and not just appearing out of nowhere.


```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) because
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 treats 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):

Choose a reason for hiding this comment

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

Suggested change
or [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding):
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 provides these default implementations only 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:

Choose a reason for hiding this comment

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

Suggested change
addition to a preferred name:
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 selects the 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:

Choose a reason for hiding this comment

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

Suggested change
that failure:
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 _doesn't_ 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:

Choose a reason for hiding this comment

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

Suggested change
usage:
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:
Comment on lines +152 to +153

Choose a reason for hiding this comment

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

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Holding for feedback on : vs .


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

borrowing func preferredName(
for attachment: borrowing Attachment<Self>,
basedOn suggestedName: String
) -> String {
if suggestedName.contains(".") {
// The name already contains a path extension, so don't 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.

If you do not pass the `--attachments-path` option, the testing library does
not save any attachments you record.


## Topics

### Attaching values to tests
Expand Down
Loading