diff --git a/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift b/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift index 8ca2a0ae4..e51742dc3 100644 --- a/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift +++ b/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift @@ -20,22 +20,21 @@ private import ImageIO /// initializers on ``Testing/Attachment`` that take instances of such types and /// handle converting them to image data when needed. /// -/// The following system-provided image types conform to this protocol and can -/// be attached to a test: +/// You can attach instances of the following system-provided image types to a +/// test: /// -/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) -/// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) -/// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) -/// (macOS) -/// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) -/// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) +/// | 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. @_spi(Experimental) @available(_uttypesAPI, *) -public protocol AttachableAsCGImage { +public protocol AttachableAsCGImage: SendableMetatype { /// An instance of `CGImage` representing this image. /// /// - Throws: Any error that prevents the creation of an image. diff --git a/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift b/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift index 866240e64..b3349915b 100644 --- a/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift +++ b/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift @@ -26,15 +26,14 @@ extension Attachment { /// This value is used when recording issues associated with the /// attachment. /// - /// The following system-provided image types conform to the - /// ``AttachableAsCGImage`` protocol and can be attached to a test: + /// You can attach instances of the following system-provided image types to a + /// test: /// - /// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) - /// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) - /// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) - /// (macOS) - /// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) - /// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) + /// | 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 @@ -48,8 +47,12 @@ extension Attachment { named preferredName: String? = nil, as imageFormat: AttachableImageFormat? = nil, sourceLocation: SourceLocation = #_sourceLocation - ) where AttachableValue == _AttachableImageWrapper { - let imageWrapper = _AttachableImageWrapper(image: image, imageFormat: imageFormat) + ) where T: AttachableAsCGImage, AttachableValue == _AttachableImageWrapper { + let imageWrapper = _AttachableImageWrapper( + image: image._copyAttachableValue(), + imageFormat: imageFormat, + deinitializingWith: { _ in } + ) self.init(imageWrapper, named: preferredName, sourceLocation: sourceLocation) } @@ -64,17 +67,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. + /// and immediately attaches it to the current test. You can attach instances + /// of the following system-provided image types to a test: /// - /// The following system-provided image types conform to the - /// ``AttachableAsCGImage`` protocol and can be attached to a test: - /// - /// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) - /// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) - /// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) - /// (macOS) - /// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) - /// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) + /// | 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 @@ -88,19 +88,20 @@ extension Attachment { named preferredName: String? = nil, as imageFormat: AttachableImageFormat? = nil, sourceLocation: SourceLocation = #_sourceLocation - ) where AttachableValue == _AttachableImageWrapper { + ) where T: AttachableAsCGImage, AttachableValue == _AttachableImageWrapper { let attachment = Self(image, named: preferredName, as: imageFormat, sourceLocation: sourceLocation) Self.record(attachment, sourceLocation: sourceLocation) } } +// MARK: - + @_spi(Experimental) // STOP: not part of ST-0014 @available(_uttypesAPI, *) extension Attachment where AttachableValue: AttachableWrapper, AttachableValue.Wrapped: AttachableAsCGImage { /// The image format to use when encoding the represented image. - @_disfavoredOverload - public var imageFormat: AttachableImageFormat? { - // FIXME: no way to express `where AttachableValue == _AttachableImageWrapper` on a property + @_disfavoredOverload public var imageFormat: AttachableImageFormat? { + // FIXME: no way to express `where AttachableValue == _AttachableImageWrapper` on a property (see rdar://47559973) (attachableValue as? _AttachableImageWrapper)?.imageFormat } } diff --git a/Sources/Overlays/_Testing_CoreGraphics/Attachments/ImageAttachmentError.swift b/Sources/Overlays/_Testing_CoreGraphics/Attachments/ImageAttachmentError.swift deleted file mode 100644 index f957888b7..000000000 --- a/Sources/Overlays/_Testing_CoreGraphics/Attachments/ImageAttachmentError.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for Swift project authors -// - -#if SWT_TARGET_OS_APPLE && canImport(CoreGraphics) -/// A type representing an error that can occur when attaching an image. -package enum ImageAttachmentError: Error, CustomStringConvertible { - /// The image could not be converted to an instance of `CGImage`. - case couldNotCreateCGImage - - /// The image destination could not be created. - case couldNotCreateImageDestination - - /// The image could not be converted. - case couldNotConvertImage - - public var description: String { - switch self { - case .couldNotCreateCGImage: - "Could not create the corresponding Core Graphics image." - case .couldNotCreateImageDestination: - "Could not create the Core Graphics image destination to encode this image." - case .couldNotConvertImage: - "Could not convert the image to the specified format." - } - } -} -#endif diff --git a/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper.swift b/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper+AttachableWrapper.swift similarity index 66% rename from Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper.swift rename to Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper+AttachableWrapper.swift index 61904936f..bb0d42b23 100644 --- a/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper.swift +++ b/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper+AttachableWrapper.swift @@ -13,7 +13,7 @@ private import CoreGraphics private import ImageIO -import UniformTypeIdentifiers +private import UniformTypeIdentifiers /// ## Why can't images directly conform to Attachable? /// @@ -38,53 +38,13 @@ import UniformTypeIdentifiers /// (And no, the language does not let us write `where T: Self` anywhere /// useful.) -/// A wrapper type for image types such as `CGImage` and `NSImage` that can be -/// attached indirectly. -/// -/// You do not need to use this type directly. Instead, initialize an instance -/// of ``Attachment`` using an instance of an image type that conforms to -/// ``AttachableAsCGImage``. The following system-provided image types conform -/// to the ``AttachableAsCGImage`` protocol and can be attached to a test: -/// -/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) -/// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) -/// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) -/// (macOS) -/// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) -/// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) -@_spi(Experimental) @available(_uttypesAPI, *) -public final class _AttachableImageWrapper: Sendable where Image: AttachableAsCGImage { - /// The underlying image. - /// - /// `CGImage` and `UIImage` are sendable, but `NSImage` is not. `NSImage` - /// instances can be created from closures that are run at rendering time. - /// The AppKit cross-import overlay is responsible for ensuring that any - /// instances of this type it creates hold "safe" `NSImage` instances. - nonisolated(unsafe) let image: Image - - /// The image format to use when encoding the represented image. - let imageFormat: AttachableImageFormat? - - init(image: Image, imageFormat: AttachableImageFormat?) { - self.image = image._copyAttachableValue() - self.imageFormat = imageFormat - } -} - -// MARK: - - -@available(_uttypesAPI, *) -extension _AttachableImageWrapper: AttachableWrapper { - public var wrappedValue: Image { - image - } - +extension _AttachableImageWrapper: Attachable, AttachableWrapper where Image: AttachableAsCGImage { public func withUnsafeBytes(for attachment: borrowing Attachment<_AttachableImageWrapper>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { let data = NSMutableData() // Convert the image to a CGImage. - let attachableCGImage = try image.attachableCGImage + let attachableCGImage = try wrappedValue.attachableCGImage // Create the image destination. let contentType = AttachableImageFormat.computeContentType(for: imageFormat, withPreferredName: attachment.preferredName) @@ -93,8 +53,8 @@ extension _AttachableImageWrapper: AttachableWrapper { } // Configure the properties of the image conversion operation. - let orientation = image._attachmentOrientation - let scaleFactor = image._attachmentScaleFactor + let orientation = wrappedValue._attachmentOrientation + let scaleFactor = wrappedValue._attachmentScaleFactor let properties: [CFString: Any] = [ kCGImageDestinationLossyCompressionQuality: CGFloat(imageFormat?.encodingQuality ?? 1.0), kCGImagePropertyOrientation: orientation, diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableAsIWICBitmapSource.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableAsIWICBitmapSource.swift index cf23df63a..fdcad1809 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableAsIWICBitmapSource.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableAsIWICBitmapSource.swift @@ -9,8 +9,7 @@ // #if os(Windows) -@_spi(Experimental) import Testing - +private import Testing public import WinSDK /// A protocol describing images that can be converted to instances of @@ -21,13 +20,14 @@ public import WinSDK /// initializers on ``Testing/Attachment`` that take instances of such types and /// handle converting them to image data when needed. /// -/// The following system-provided image types conform to this protocol and can -/// be attached to a test: +/// You can attach instances of the following system-provided image types to a +/// test: /// -/// - [`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) +/// | 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, @@ -93,26 +93,27 @@ public protocol _AttachableByAddressAsIWICBitmapSource { } /// A protocol describing images that can be converted to instances of -/// ``Testing/Attachment``. +/// [`Attachment`](https://developer.apple.com/documentation/testing/attachment). /// /// Instances of types conforming to this protocol do not themselves conform to -/// ``Testing/Attachable``. Instead, the testing library provides additional -/// initializers on ``Testing/Attachment`` that take instances of such types and -/// handle converting them to image data when needed. +/// [`Attachable`](https://developer.apple.com/documentation/testing/attachable). +/// 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. /// -/// The following system-provided image types conform to this protocol and can -/// be attached to a test: +/// You can attach instances of the following system-provided image types to a +/// test: /// -/// - [`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) +/// | 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. @_spi(Experimental) -public protocol AttachableAsIWICBitmapSource { +public protocol AttachableAsIWICBitmapSource: SendableMetatype { /// Create a WIC bitmap source representing an instance of this type. /// /// - Returns: A pointer to a new WIC bitmap source representing this image. diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableImageFormat+CLSID.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableImageFormat+CLSID.swift index 009015e13..1881fa036 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableImageFormat+CLSID.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/AttachableImageFormat+CLSID.swift @@ -9,8 +9,7 @@ // #if os(Windows) -@_spi(Experimental) import Testing - +@_spi(Experimental) public import Testing public import WinSDK extension AttachableImageFormat { @@ -258,7 +257,7 @@ extension AttachableImageFormat { /// /// If `clsid` does not represent an image encoder type supported by WIC, the /// result is undefined. For a list of image encoders supported by WIC, see - /// the documentation for the [IWICBitmapEncoder](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder) + /// the documentation for the [`IWICBitmapEncoder`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder) /// class. public init(_ clsid: CLSID, encodingQuality: Float = 1.0) { self.init(kind: .systemValue(clsid), encodingQuality: encodingQuality) diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/Attachment+AttachableAsIWICBitmapSource.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/Attachment+AttachableAsIWICBitmapSource.swift index ec0bb016b..8068e7a8e 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/Attachment+AttachableAsIWICBitmapSource.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/Attachment+AttachableAsIWICBitmapSource.swift @@ -26,13 +26,14 @@ extension Attachment { /// This value is used when recording issues associated with the /// attachment. /// - /// The following system-provided image types conform to the - /// ``AttachableAsIWICBitmapSource`` protocol and can be attached to a test: + /// You can attach instances of the following system-provided image types to a + /// test: /// - /// - [`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) + /// | 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 @@ -46,8 +47,12 @@ extension Attachment { named preferredName: String? = nil, as imageFormat: AttachableImageFormat? = nil, sourceLocation: SourceLocation = #_sourceLocation - ) where AttachableValue == _AttachableImageWrapper { - let imageWrapper = _AttachableImageWrapper(image: image, imageFormat: imageFormat) + ) where T: AttachableAsIWICBitmapSource, AttachableValue == _AttachableImageWrapper { + let imageWrapper = _AttachableImageWrapper( + image: image._copyAttachableValue(), + imageFormat: imageFormat, + deinitializingWith: { $0._deinitializeAttachableValue() } + ) self.init(imageWrapper, named: preferredName, sourceLocation: sourceLocation) } @@ -64,15 +69,14 @@ extension Attachment { /// attachment. /// /// This function creates a new instance of ``Attachment`` wrapping `image` - /// and immediately attaches it to the current test. + /// and immediately attaches it to the current test. You can attach instances + /// of the following system-provided image types to a test: /// - /// The following system-provided image types conform to the - /// ``AttachableAsIWICBitmapSource`` protocol and can be attached to a test: - /// - /// - [`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) + /// | 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 @@ -86,9 +90,8 @@ extension Attachment { named preferredName: String? = nil, as imageFormat: AttachableImageFormat? = nil, sourceLocation: SourceLocation = #_sourceLocation - ) where AttachableValue == _AttachableImageWrapper { - let imageWrapper = _AttachableImageWrapper(image: image, imageFormat: imageFormat) - let attachment = Self(imageWrapper, named: preferredName, sourceLocation: sourceLocation) + ) where T: AttachableAsIWICBitmapSource, AttachableValue == _AttachableImageWrapper { + let attachment = Self(image, named: preferredName, as: imageFormat, sourceLocation: sourceLocation) Self.record(attachment, sourceLocation: sourceLocation) } } @@ -96,9 +99,8 @@ extension Attachment { @_spi(Experimental) extension Attachment where AttachableValue: AttachableWrapper, AttachableValue.Wrapped: AttachableAsIWICBitmapSource { /// The image format to use when encoding the represented image. - @_disfavoredOverload - public var imageFormat: AttachableImageFormat? { - // FIXME: no way to express `where AttachableValue == _AttachableImageWrapper` on a property + @_disfavoredOverload public var imageFormat: AttachableImageFormat? { + // FIXME: no way to express `where AttachableValue == _AttachableImageWrapper` on a property (see rdar://47559973) (attachableValue as? _AttachableImageWrapper)?.imageFormat } } diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/HBITMAP+AttachableAsIWICBitmapSource.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/HBITMAP+AttachableAsIWICBitmapSource.swift index 0d11fd0bc..4baef8d36 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/HBITMAP+AttachableAsIWICBitmapSource.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/HBITMAP+AttachableAsIWICBitmapSource.swift @@ -9,8 +9,7 @@ // #if os(Windows) -import Testing - +private import Testing public import WinSDK @_spi(Experimental) diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/HICON+AttachableAsIWICBitmapSource.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/HICON+AttachableAsIWICBitmapSource.swift index a25e8f371..36f4fcc9e 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/HICON+AttachableAsIWICBitmapSource.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/HICON+AttachableAsIWICBitmapSource.swift @@ -9,8 +9,7 @@ // #if os(Windows) -import Testing - +private import Testing public import WinSDK @_spi(Experimental) diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/IWICBitmapSource+AttachableAsIWICBitmapSource.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/IWICBitmapSource+AttachableAsIWICBitmapSource.swift index 401e6f480..8ff6d5430 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/IWICBitmapSource+AttachableAsIWICBitmapSource.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/IWICBitmapSource+AttachableAsIWICBitmapSource.swift @@ -9,8 +9,7 @@ // #if os(Windows) -import Testing - +private import Testing public import WinSDK /// - Important: The casts in this file to `IUnknown` are safe insofar as we use diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/UnsafeMutablePointer+AttachableAsIWICBitmapSource.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/UnsafeMutablePointer+AttachableAsIWICBitmapSource.swift index 6d34a6e90..297e1f25a 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/UnsafeMutablePointer+AttachableAsIWICBitmapSource.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/UnsafeMutablePointer+AttachableAsIWICBitmapSource.swift @@ -9,8 +9,7 @@ // #if os(Windows) -import Testing - +private import Testing public import WinSDK @_spi(Experimental) diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/_AttachableImageWrapper.swift b/Sources/Overlays/_Testing_WinSDK/Attachments/_AttachableImageWrapper+AttachableWrapper.swift similarity index 76% rename from Sources/Overlays/_Testing_WinSDK/Attachments/_AttachableImageWrapper.swift rename to Sources/Overlays/_Testing_WinSDK/Attachments/_AttachableImageWrapper+AttachableWrapper.swift index 63931c378..7cfe181b8 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/_AttachableImageWrapper.swift +++ b/Sources/Overlays/_Testing_WinSDK/Attachments/_AttachableImageWrapper+AttachableWrapper.swift @@ -10,47 +10,9 @@ #if os(Windows) @_spi(Experimental) public import Testing +private import WinSDK -internal import WinSDK - -/// A wrapper type for image types such as `HBITMAP` and `HICON` that can be -/// attached indirectly. -/// -/// You do not need to use this type directly. Instead, initialize an instance -/// of ``Attachment`` using an instance of an image type that conforms to -/// ``AttachableAsIWICBitmapSource``. The following system-provided image types -/// conform to the ``AttachableAsIWICBitmapSource`` protocol and can be attached -/// to a test: -/// -/// - [`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) -@_spi(Experimental) -public final class _AttachableImageWrapper: Sendable where Image: AttachableAsIWICBitmapSource { - /// The underlying image. - nonisolated(unsafe) let image: Image - - /// The image format to use when encoding the represented image. - let imageFormat: AttachableImageFormat? - - init(image: borrowing Image, imageFormat: AttachableImageFormat?) { - self.image = image._copyAttachableValue() - self.imageFormat = imageFormat - } - - deinit { - image._deinitializeAttachableValue() - } -} - -// MARK: - - -extension _AttachableImageWrapper: AttachableWrapper { - public var wrappedValue: Image { - image - } - +extension _AttachableImageWrapper: Attachable, AttachableWrapper where Image: AttachableAsIWICBitmapSource { public func withUnsafeBytes(for attachment: borrowing Attachment<_AttachableImageWrapper>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { // Create an in-memory stream to write the image data to. Note that Windows // documentation recommends SHCreateMemStream() instead, but that function @@ -71,7 +33,7 @@ extension _AttachableImageWrapper: AttachableWrapper { } // Create the bitmap and downcast it to an IWICBitmapSource for later use. - let bitmap = try image._copyAttachableIWICBitmapSource(using: factory) + let bitmap = try wrappedValue._copyAttachableIWICBitmapSource(using: factory) defer { _ = bitmap.pointee.lpVtbl.pointee.Release(bitmap) } diff --git a/Sources/Testing/Attachments/AttachableImageFormat.swift b/Sources/Testing/Attachments/Images/AttachableImageFormat.swift similarity index 100% rename from Sources/Testing/Attachments/AttachableImageFormat.swift rename to Sources/Testing/Attachments/Images/AttachableImageFormat.swift diff --git a/Sources/Overlays/_Testing_WinSDK/Attachments/ImageAttachmentError.swift b/Sources/Testing/Attachments/Images/ImageAttachmentError.swift similarity index 54% rename from Sources/Overlays/_Testing_WinSDK/Attachments/ImageAttachmentError.swift rename to Sources/Testing/Attachments/Images/ImageAttachmentError.swift index 1b37df4a9..7cd6f8180 100644 --- a/Sources/Overlays/_Testing_WinSDK/Attachments/ImageAttachmentError.swift +++ b/Sources/Testing/Attachments/Images/ImageAttachmentError.swift @@ -1,38 +1,56 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Copyright (c) 2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for Swift project authors // -#if os(Windows) -@_spi(Experimental) import Testing - -internal import WinSDK +private import _TestingInternals /// A type representing an error that can occur when attaching an image. -enum ImageAttachmentError: Error { +package enum ImageAttachmentError: Error { +#if SWT_TARGET_OS_APPLE + /// The image could not be converted to an instance of `CGImage`. + case couldNotCreateCGImage + + /// The image destination could not be created. + case couldNotCreateImageDestination + + /// The image could not be converted. + case couldNotConvertImage +#elseif os(Windows) /// A call to `QueryInterface()` failed. - case queryInterfaceFailed(Any.Type, HRESULT) + case queryInterfaceFailed(Any.Type, Int32) /// The testing library failed to create a COM object. - case comObjectCreationFailed(Any.Type, HRESULT) + case comObjectCreationFailed(Any.Type, Int32) /// An image could not be written. - case imageWritingFailed(HRESULT) + case imageWritingFailed(Int32) /// The testing library failed to get an in-memory stream's underlying buffer. - case globalFromStreamFailed(HRESULT) + case globalFromStreamFailed(Int32) /// A property could not be written to a property bag. - case propertyBagWritingFailed(String, HRESULT) + case propertyBagWritingFailed(String, Int32) +#endif } extension ImageAttachmentError: CustomStringConvertible { - var description: String { + package var description: String { +#if SWT_TARGET_OS_APPLE + switch self { + case .couldNotCreateCGImage: + "Could not create the corresponding Core Graphics image." + case .couldNotCreateImageDestination: + "Could not create the Core Graphics image destination to encode this image." + case .couldNotConvertImage: + "Could not convert the image to the specified format." + } +#elseif os(Windows) switch self { case let .queryInterfaceFailed(type, result): "Could not cast a COM object to type '\(type)' (HRESULT \(result))." @@ -45,6 +63,8 @@ extension ImageAttachmentError: CustomStringConvertible { case let .propertyBagWritingFailed(name, result): "Could not set the property '\(name)' (HRESULT \(result))." } +#else + swt_unreachable() +#endif } } -#endif diff --git a/Sources/Testing/Attachments/Images/_AttachableImageWrapper.swift b/Sources/Testing/Attachments/Images/_AttachableImageWrapper.swift new file mode 100644 index 000000000..71623538a --- /dev/null +++ b/Sources/Testing/Attachments/Images/_AttachableImageWrapper.swift @@ -0,0 +1,49 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +/// 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) | +@_spi(Experimental) +@available(_uttypesAPI, *) +public final class _AttachableImageWrapper: Sendable { + /// The underlying image. + private nonisolated(unsafe) let _image: Image + + /// The image format to use when encoding the represented image. + package let imageFormat: AttachableImageFormat? + + /// A deinitializer function to call to clean up `image`. + private let _deinit: @Sendable (consuming Image) -> Void + + package init(image: Image, imageFormat: AttachableImageFormat?, deinitializingWith `deinit`: @escaping @Sendable (consuming Image) -> Void) { + self._image = image + self.imageFormat = imageFormat + self._deinit = `deinit` + } + + deinit { + _deinit(_image) + } +} + +@available(_uttypesAPI, *) +extension _AttachableImageWrapper { + public var wrappedValue: Image { + _image + } +} diff --git a/Sources/Testing/CMakeLists.txt b/Sources/Testing/CMakeLists.txt index 36437e167..531d27cfc 100644 --- a/Sources/Testing/CMakeLists.txt +++ b/Sources/Testing/CMakeLists.txt @@ -21,6 +21,9 @@ add_library(Testing ABI/Encoded/ABI.EncodedIssue.swift ABI/Encoded/ABI.EncodedMessage.swift ABI/Encoded/ABI.EncodedTest.swift + Attachments/Images/_AttachableImageWrapper.swift + Attachments/Images/AttachableImageFormat.swift + Attachments/Images/ImageAttachmentError.swift Attachments/Attachable.swift Attachments/AttachableWrapper.swift Attachments/Attachment.swift