Skip to content

Commit d630017

Browse files
grynspanjgmcnutt
andauthored
Provide prose documentation for attachments. (#1413)
This PR adds prose documentation explaining how to create attachments and add them to tests. Resolves #1143. ### 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. --------- Co-authored-by: jgmcnutt <jgmcnutt@me.com>
1 parent b75d75a commit d630017

File tree

7 files changed

+183
-106
lines changed

7 files changed

+183
-106
lines changed

Sources/Testing/Attachments/Attachment.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,6 @@ private import _TestingInternals
1919
/// record the attachment, call ``Attachment/record(_:sourceLocation:)``.
2020
/// Alternatively, pass your attachable value directly to ``Attachment/record(_:named:sourceLocation:)``.
2121
///
22-
/// By default, the testing library saves your attachments as soon as you call
23-
/// ``Attachment/record(_:sourceLocation:)`` or
24-
/// ``Attachment/record(_:named:sourceLocation:)``. You can access saved
25-
/// attachments after your tests finish running:
26-
///
27-
/// - When using Xcode, you can access attachments from the test report.
28-
/// - When using Visual Studio Code, the testing library saves attachments to
29-
/// `.build/attachments` by default. Visual Studio Code reports the paths to
30-
/// individual attachments in its Tests Results panel.
31-
/// - When using Swift Package Manager's `swift test` command, you can pass the
32-
/// `--attachments-path` option. The testing library saves attachments to the
33-
/// specified directory.
34-
///
3522
/// @Metadata {
3623
/// @Available(Swift, introduced: 6.2)
3724
/// @Available(Xcode, introduced: 26.0)

Sources/Testing/Attachments/Images/AttachableAsImage.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,6 @@
4545
/// Instead, the testing library provides additional initializers on [`Attachment`](https://developer.apple.com/documentation/testing/attachment)
4646
/// that take instances of such types and handle converting them to image data when needed.
4747
///
48-
/// You can attach instances of the following system-provided image types to a
49-
/// test:
50-
///
51-
/// | Platform | Supported Types |
52-
/// |-|-|
53-
/// | 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) |
54-
/// | 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) |
55-
/// | 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) |
56-
///
5748
/// You do not generally need to add your own conformances to this protocol. If
5849
/// you have an image in another format that needs to be attached to a test,
5950
/// first convert it to an instance of one of the types above.

Sources/Testing/Attachments/Images/AttachableImageFormat.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
/// instance of this type, the testing library infers which format to use based
1818
/// on the attachment's preferred name.
1919
///
20-
/// The PNG and JPEG image formats are always supported. The set of additional
21-
/// supported image formats is platform-specific:
20+
/// The testing library always supports the PNG and JPEG image formats. The set
21+
/// of additional supported image formats is platform-specific:
2222
///
2323
/// - On Apple platforms, you can use [`CGImageDestinationCopyTypeIdentifiers()`](https://developer.apple.com/documentation/imageio/cgimagedestinationcopytypeidentifiers())
2424
/// from the [Image I/O framework](https://developer.apple.com/documentation/imageio)

Sources/Testing/Attachments/Images/Attachment+AttachableAsImage.swift

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,6 @@ extension Attachment {
2929
/// This value is used when recording issues associated with the
3030
/// attachment.
3131
///
32-
/// You can attach instances of the following system-provided image types to a
33-
/// test:
34-
///
35-
/// | Platform | Supported Types |
36-
/// |-|-|
37-
/// | 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) |
38-
/// | 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) |
39-
/// | 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) |
40-
///
4132
/// The testing library uses the image format specified by `imageFormat`. Pass
4233
/// `nil` to let the testing library decide which image format to use. If you
4334
/// pass `nil`, then the image format that the testing library uses depends on
@@ -70,22 +61,14 @@ extension Attachment {
7061
/// - sourceLocation: The source location of the call to this function.
7162
///
7263
/// This function creates a new instance of ``Attachment`` wrapping `image`
73-
/// and immediately attaches it to the current test. You can attach instances
74-
/// of the following system-provided image types to a test:
75-
///
76-
/// | Platform | Supported Types |
77-
/// |-|-|
78-
/// | 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) |
79-
/// | 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) |
80-
/// | 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) |
81-
///
82-
/// The testing library uses the image format specified by `imageFormat`. Pass
83-
/// `nil` to let the testing library decide which image format to use. If you
84-
/// pass `nil`, then the image format that the testing library uses depends on
85-
/// the path extension you specify in `preferredName`, if any. If you do not
86-
/// specify a path extension, or if the path extension you specify doesn't
87-
/// correspond to an image format the operating system knows how to write, the
88-
/// testing library selects an appropriate image format for you.
64+
/// and immediately attaches it to the current test. The testing library uses
65+
/// the image format that `imageFormat` specifies. Pass `nil` to let the testing
66+
/// library select which image format to use. If you pass `nil`, the
67+
/// image format that the testing library uses depends on the path extension
68+
/// you specify in `preferredName`, if any. If you don't specify a path
69+
/// extension, or if the path extension you specify doesn't correspond to an
70+
/// image format the operating system knows how to write, the testing library
71+
/// selects an appropriate image format for you.
8972
///
9073
/// @Metadata {
9174
/// @Available(Swift, introduced: 6.3)

Sources/Testing/Attachments/Images/_AttachableImageWrapper.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,6 @@
99
//
1010

1111
/// A wrapper type for images that can be indirectly attached to a test.
12-
///
13-
/// You can attach instances of the following system-provided image types to a
14-
/// test:
15-
///
16-
/// | Platform | Supported Types |
17-
/// |-|-|
18-
/// | 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) |
19-
/// | 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) |
20-
/// | 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) |
2112
#if SWT_NO_IMAGE_ATTACHMENTS
2213
@_unavailableInEmbedded
2314
@available(*, unavailable, message: "Image attachments are not available on this platform.")

Sources/Testing/Testing.docc/Attachments.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,179 @@ Attach values to tests to help diagnose issues and gather feedback.
1717
Attach values such as strings and files to tests. Implement the ``Attachable``
1818
protocol to create your own attachable types.
1919

20+
### Attach data or strings
21+
22+
If your test produces encoded data that you want to save as an attachment, you
23+
can call ``Attachment/record(_:named:sourceLocation:)``.
24+
25+
```swift
26+
struct SalesReport { ... }
27+
28+
@Test func `sales report adds up`() async throws {
29+
let salesReport = await generateSalesReport()
30+
try salesReport.validate()
31+
let bytes: [UInt8] = try salesReport.convertToCSV()
32+
Attachment.record(bytes, named: "sales report.csv")
33+
}
34+
```
35+
36+
You can attach an instance of [`Array<UInt8>`](https://developer.apple.com/documentation/swift/array),
37+
[`ContiguousArray<UInt8>`](https://developer.apple.com/documentation/swift/contiguousarray),
38+
[`ArraySlice<UInt8>`](https://developer.apple.com/documentation/swift/arrayslice),
39+
or [`Data`](https://developer.apple.com/documentation/foundation/data) because
40+
these types automatically conform to ``Attachable``.
41+
42+
You can also attach an instance of [`String`](https://developer.apple.com/documentation/swift/string)
43+
or [`Substring`](https://developer.apple.com/documentation/swift/substring). The
44+
testing library treats attached strings as UTF-8 text
45+
files. If you want to save a string as an attachment using a different encoding,
46+
convert it to [`Data`](https://developer.apple.com/documentation/foundation/data)
47+
using [`data(using:allowLossyConversion:)`](https://developer.apple.com/documentation/swift/stringprotocol/data(using:allowlossyconversion:))
48+
and attach the resulting data instead of the original string.
49+
50+
### Attach encodable values
51+
52+
If you have a value you want to save as an attachment that conforms to either
53+
[`Encodable`](https://developer.apple.com/documentation/swift/encodable) or
54+
[`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding),
55+
you can extend it to add conformance to ``Attachable``. When you import the
56+
[Foundation](https://developer.apple.com/documentation/foundation) module, the
57+
testing library automatically provides a default implementation of
58+
``Attachable`` to types that also conform to [`Encodable`](https://developer.apple.com/documentation/swift/encodable)
59+
or [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding).
60+
61+
```swift
62+
import Testing
63+
import Foundation
64+
65+
struct SalesReport { ... }
66+
extension SalesReport: Encodable, Attachable {}
67+
68+
@Test func `sales report adds up`() async throws {
69+
let salesReport = await generateSalesReport()
70+
try salesReport.validate()
71+
Attachment.record(salesReport, named: "sales report.json")
72+
}
73+
```
74+
75+
- Important: The testing library provides these default implementations only if
76+
your test target imports the [Foundation](https://developer.apple.com/documentation/foundation)
77+
module.
78+
79+
### Attach images
80+
81+
You can attach instances of the following system-provided image types to a test:
82+
83+
| Platform | Supported types |
84+
|-|-|
85+
| 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) |
86+
| 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) |
87+
| watchOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
88+
| 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) |
89+
90+
When you attach an image to a test, you can specify the image format to use in
91+
addition to a preferred name.
92+
93+
```swift
94+
struct SalesReport { ... }
95+
96+
@Test func `sales report adds up`() async throws {
97+
let salesReport = await generateSalesReport()
98+
let image = try salesReport.renderTrendsGraph()
99+
Attachment.record(image, named: "sales report", as: .png)
100+
}
101+
```
102+
103+
If you don't specify an image format when attaching an image to a test, the
104+
testing library selects the format to use based on the preferred name you pass.
105+
106+
### Attach other values
107+
108+
If you have a value that needs a custom encoded representation when you save it
109+
as an attachment, implement ``Attachable/withUnsafeBytes(for:_:)``. The
110+
implementation of this function calls its `body` argument and passes the encoded
111+
representation of `self` or, if a failure occurs, throws an error representing
112+
that failure.
113+
114+
```swift
115+
struct SalesReport { ... }
116+
117+
extension SalesReport: Attachable {
118+
borrowing func withUnsafeBytes<R>(
119+
for attachment: borrowing Attachment<Self>,
120+
_ body: (UnsafeRawBufferPointer) throws -> R
121+
) throws -> R {
122+
let bytes = try salesReport.convertToCSV() // might fail to convert to CSV
123+
try bytes.withUnsafeBytes { buffer in // rethrows any error from `body`
124+
try body(buffer)
125+
}
126+
}
127+
}
128+
```
129+
130+
If your type conforms to [`Sendable`](https://developer.apple.com/documentation/swift/sendable),
131+
the testing library avoids calling this function until it needs to save the
132+
attachment. If your type _doesn't_ conform to [`Sendable`](https://developer.apple.com/documentation/swift/sendable),
133+
the testing library calls this function as soon as you record the attachment.
134+
135+
#### Customize attachment behavior
136+
137+
If you can reliably estimate in advance how large the encoded representation
138+
will be, implement ``Attachable/estimatedAttachmentByteCount``. The testing
139+
library uses the value of this property as a hint to optimize memory and disk
140+
usage.
141+
142+
```swift
143+
extension SalesReport: Attachable {
144+
...
145+
146+
var estimatedAttachmentByteCount: Int? {
147+
return self.entries.count * 123
148+
}
149+
}
150+
```
151+
152+
You can also implement ``Attachable/preferredName(for:basedOn:)`` if you want to
153+
customize the name of the attachment when saving it.
154+
155+
```swift
156+
extension SalesReport: Attachable {
157+
...
158+
159+
borrowing func preferredName(
160+
for attachment: borrowing Attachment<Self>,
161+
basedOn suggestedName: String
162+
) -> String {
163+
if suggestedName.contains(".") {
164+
// The name already contains a path extension, so don't append another.
165+
return suggestedName
166+
}
167+
168+
// Append ".csv" to the name so the resulting file opens as a spreadsheet.
169+
return "\(suggestedName).csv"
170+
}
171+
}
172+
```
173+
174+
### Inspect attachments after a test run ends
175+
176+
By default, the testing library saves your attachments as soon as you call
177+
``Attachment/record(_:sourceLocation:)`` or
178+
``Attachment/record(_:named:sourceLocation:)``. You can access saved attachments
179+
after your tests finish running:
180+
181+
- When using Xcode, you can access attachments from the test report.
182+
- When using Visual Studio Code, the testing library saves attachments to
183+
`.build/attachments` by default. Visual Studio Code reports the paths to
184+
individual attachments in its Tests Results panel.
185+
- When using Swift Package Manager's `swift test` command, you can pass the
186+
`--attachments-path` option. The testing library saves attachments to the
187+
specified directory.
188+
189+
If you do not pass the `--attachments-path` option, the testing library does
190+
not save any attachments you record.
191+
192+
20193
## Topics
21194

22195
### Attaching values to tests

0 commit comments

Comments
 (0)