diff --git a/proposals/testing/NNNN-image-attachment-adjustments.md b/proposals/testing/NNNN-image-attachment-adjustments.md new file mode 100644 index 0000000000..600b0d155b --- /dev/null +++ b/proposals/testing/NNNN-image-attachment-adjustments.md @@ -0,0 +1,166 @@ +# Adjustments to image attachments in Swift Testing + +* Proposal: [ST-NNNN](NNNN-image-attachment-adjustments.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift-testing#1359](https://github.com/swiftlang/swift-testing/pull/1359) +* Review: ([pitch](https://forums.swift.org/...)) + +## Introduction + +This proposal includes a small number of adjustments to the API surface of Swift +Testing's image attachments feature introduced in [ST-0014](0014-image-attachments-in-swift-testing-apple-platforms.md) +and [ST-0015](0015-image-attachments-in-swift-testing-windows.md). + +## Motivation + +These changes will help to align the platform-specific interfaces of the feature +more closely. + +## Proposed solution + +The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are +combined into a single protocol, `AttachableAsImage` with adjusted protocol +requirements; a change is made to `AttachableImageFormat` to more closely +align its interface between Darwin and Windows; `AttachableImageFormat` is made +to conform to `Equatable` and `Hashable`; and an additional property is added to +`Attachment` to query its image format. + +## Detailed design + +The following changes are proposed: + +### Combining AttachableAsCGImage and AttachableAsIWICBitmapSource + +The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are +combined into a single protocol, `AttachableAsImage`. + +These platform-specific requirements are removed: + +```diff +- var attachableCGImage: CGImage { get throws } +- func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer +``` + +They are replaced with a new requirement that encapsulates the image encoding +operation. This requirement is implemented by the CoreGraphics and WinSDK +overlays and is made publicly available for test authors who wish to declare +additional conformances to this protocol for types that are not based on +`CGImage` or `IWICBitmapSource`: + +```swift +public protocol AttachableAsImage { + // ... + + /// Encode a representation of this image in a given image format. + /// + /// - Parameters: + /// - imageFormat: The image format to use when encoding this image. + /// - body: A function to call. A temporary buffer containing a data + /// representation of this instance is passed to it. + /// + /// - Returns: Whatever is returned by `body`. + /// + /// - Throws: Whatever is thrown by `body`, or any error that prevented the + /// creation of the buffer. + /// + /// The testing library uses this function when saving an image as an + /// attachment. The implementation should use `imageFormat` to determine what + /// encoder to use. + borrowing func withUnsafeBytes(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R +} +``` + +If a developer has an image type that should conform to `AttachableAsImage` and +wraps an instance of `CGImage` or `IWICBitmapSource`, it is straightforward for +them to delegate to that object. For example: + +```swift +import Testing +import CoreGraphics + +struct MyImage { + var cgImage: CGImage + // ... +} + +extension MyImage: AttachableAsImage { + func withUnsafeBytes(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + try cgImage.withUnsafeBytes(as: imageFormat, body) + } +} +``` + +### Adjusting AttachableImageFormat + +The following Apple-specific `AttachableImageFormat` initializer is renamed so +that its first argument has an explicit label: + +```diff + public struct AttachableImageFormat { + // ... +- public init(_ contentType: UTType, encodingQuality: Float = 1.0) ++ public init(contentType: UTType, encodingQuality: Float = 1.0) + } +``` + +This change makes the type's interface more consistent between Darwin and +Windows (where it has an `init(encoderCLSID:encodingQuality:)` initializer.) + +As well, conformances to `Equatable`, `Hashable`, `CustomStringConvertible`, and +`CustomDebugStringConvertible` are added: + +```swift +extension AttachableImageFormat: Equatable, Hashable {} +extension AttachableImageFormat: CustomStringConvertible, CustomDebugStringConvertible {} +``` + +Conformance to `Equatable` is necessary to correctly implement the +`withUnsafeBytes(as:_:)` protocol requirement mentioned above, and conformance +to `Hashable` is generally useful and straightforward to implement. Conformance +to `CustomStringConvertible` and `CustomDebugStringConvertible` allows for +better diagnostic output (especially if an encoding failure occurs.) + +### Adding an imageFormat property to Attachment + +The following property is added to `Attachment` when the attachable value is an +image: + +```swift +extension Attachment where AttachableValue: AttachableWrapper, + AttachableValue.Wrapped: AttachableAsImage { + /// The image format to use when encoding the represented image. + public var imageFormat: AttachableImageFormat? { get } +} +``` + +## Source compatibility + +These changes are breaking for anyone who has created a type that conforms to +either `AttachableAsCGImage` or `AttachableAsIWICBitmapSource`, or anyone who +has adopted `AttachableImageFormat.init(_:encodingQuality:)`. + +This feature is new in Swift 6.3 and has not shipped to developers outside of +nightly toolchain builds. As such, we feel confident that any real-world impact +to developers will be both minimal and manageable. + +## Integration with supporting tools + +No changes. + +## Future directions + +N/A + +## Alternatives considered + +- Leaving the two protocols separate. Combining them allows us to lower more + code into the main Swift Testing library and improves our ability to generate + DocC documentation, while also simplifying the story for developers who want + to use this feature across platforms. + +## Acknowledgments + +Thanks to my colleagues for their feedback on the image attachments feature and +to the Swift community for putting up with the churn!