Skip to content

Commit 0a2fb6b

Browse files
committed
Merge more of the image attachments codebase duplicated between Darwin and Windows.
This PR eliminates a bunch of duplicated code between Darwin and Windows that's used to support image attachments. We're able to do this because `_AttachableImageWrapper` is now a class and can be included in conforms-to generic constraints, so we can write `where AttachableValue: _AttachableImageWrapper<T> & AttachableWrapper`. It was previously a structure and structures can't be used in generic constraints this way. There's a new public, underscored protocol introduced here, `_AttachableAsImage`, that serves as a "base" protocol for `AttachableAsCGImage` and `AttachableAsIWICBitmapSource`. I intend to promote this protocol to API, but it will need a Swift Evolution proposal first. I'm going to include it in a future "image attachments refinement" proposal that will cover a few other things.
1 parent 2c66d9e commit 0a2fb6b

10 files changed

+69
-181
lines changed

Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private import ImageIO
3939
/// @Available(Swift, introduced: 6.3)
4040
/// }
4141
@available(_uttypesAPI, *)
42-
public protocol AttachableAsCGImage: SendableMetatype {
42+
public protocol AttachableAsCGImage: _AttachableAsImage, SendableMetatype {
4343
/// An instance of `CGImage` representing this image.
4444
///
4545
/// - Throws: Any error that prevents the creation of an image.
@@ -68,22 +68,6 @@ public protocol AttachableAsCGImage: SendableMetatype {
6868
/// This property is not part of the public interface of the testing
6969
/// library. It may be removed in a future update.
7070
var _attachmentScaleFactor: CGFloat { get }
71-
72-
/// Make a copy of this instance to pass to an attachment.
73-
///
74-
/// - Returns: A copy of `self`, or `self` if no copy is needed.
75-
///
76-
/// The testing library uses this function to take ownership of image
77-
/// resources that test authors pass to it. If possible, make a copy of or add
78-
/// a reference to `self`. If this type does not support making copies, return
79-
/// `self` verbatim.
80-
///
81-
/// The default implementation of this function when `Self` conforms to
82-
/// `Sendable` simply returns `self`.
83-
///
84-
/// This function is not part of the public interface of the testing library.
85-
/// It may be removed in a future update.
86-
func _copyAttachableValue() -> Self
8771
}
8872

8973
@available(_uttypesAPI, *)
@@ -95,6 +79,8 @@ extension AttachableAsCGImage {
9579
public var _attachmentScaleFactor: CGFloat {
9680
1.0
9781
}
82+
83+
public func _deinitializeAttachableValue() {}
9884
}
9985

10086
@available(_uttypesAPI, *)

Sources/Overlays/_Testing_CoreGraphics/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ if(APPLE)
1111
Attachments/_AttachableImageWrapper+AttachableWrapper.swift
1212
Attachments/AttachableAsCGImage.swift
1313
Attachments/AttachableImageFormat+UTType.swift
14-
Attachments/Attachment+AttachableAsCGImage.swift
1514
Attachments/CGImage+AttachableAsCGImage.swift
1615
ReexportTesting.swift)
1716

Sources/Overlays/_Testing_WinSDK/Attachments/AttachableAsIWICBitmapSource.swift

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public protocol _AttachableByAddressAsIWICBitmapSource {
113113
/// you have an image in another format that needs to be attached to a test,
114114
/// first convert it to an instance of one of the types above.
115115
@_spi(Experimental)
116-
public protocol AttachableAsIWICBitmapSource: SendableMetatype {
116+
public protocol AttachableAsIWICBitmapSource: _AttachableAsImage, SendableMetatype {
117117
/// Create a WIC bitmap source representing an instance of this type.
118118
///
119119
/// - Returns: A pointer to a new WIC bitmap source representing this image.
@@ -145,39 +145,6 @@ public protocol AttachableAsIWICBitmapSource: SendableMetatype {
145145
func _copyAttachableIWICBitmapSource(
146146
using factory: UnsafeMutablePointer<IWICImagingFactory>
147147
) throws -> UnsafeMutablePointer<IWICBitmapSource>
148-
149-
/// Make a copy of this instance.
150-
///
151-
/// - Returns: A copy of `self`, or `self` if this type does not support a
152-
/// copying operation.
153-
///
154-
/// The testing library uses this function to take ownership of image
155-
/// resources that test authors pass to it. If possible, make a copy of or add
156-
/// a reference to `self`. If this type does not support making copies, return
157-
/// `self` verbatim.
158-
///
159-
/// The default implementation of this function when `Self` conforms to
160-
/// `Sendable` simply returns `self`.
161-
///
162-
/// This function is not part of the public interface of the testing library.
163-
/// It may be removed in a future update.
164-
func _copyAttachableValue() -> Self
165-
166-
/// Manually deinitialize any resources associated with this image.
167-
///
168-
/// The implementation of this function cleans up any resources (such as
169-
/// handles or COM objects) associated with this image. The testing library
170-
/// automatically invokes this function as needed.
171-
///
172-
/// This function is not responsible for releasing the image returned from
173-
/// `_copyAttachableIWICBitmapSource(using:)`.
174-
///
175-
/// The default implementation of this function when `Self` conforms to
176-
/// `Sendable` does nothing.
177-
///
178-
/// This function is not part of the public interface of the testing library.
179-
/// It may be removed in a future update.
180-
func _deinitializeAttachableValue()
181148
}
182149

183150
extension AttachableAsIWICBitmapSource {

Sources/Overlays/_Testing_WinSDK/Attachments/Attachment+AttachableAsIWICBitmapSource.swift

Lines changed: 0 additions & 107 deletions
This file was deleted.

Sources/Overlays/_Testing_WinSDK/Attachments/UnsafeMutablePointer+AttachableAsIWICBitmapSource.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extension UnsafeMutablePointer: AttachableAsIWICBitmapSource where Pointee: _Att
3030
Pointee._copyAttachableValue(at: self)
3131
}
3232

33-
public consuming func _deinitializeAttachableValue() {
33+
public func _deinitializeAttachableValue() {
3434
Pointee._deinitializeAttachableValue(at: self)
3535
}
3636
}

Sources/Overlays/_Testing_WinSDK/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
1111
Attachments/_AttachableImageWrapper+AttachableWrapper.swift
1212
Attachments/AttachableAsIWICBitmapSource.swift
1313
Attachments/AttachableImageFormat+CLSID.swift
14-
Attachments/Attachment+AttachableAsIWICBitmapSource.swift
1514
Attachments/HBITMAP+AttachableAsIWICBitmapSource.swift
1615
Attachments/HICON+AttachableAsIWICBitmapSource.swift
1716
Attachments/IWICBitmapSource+AttachableAsIWICBitmapSource.swift
Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11-
#if SWT_TARGET_OS_APPLE && canImport(CoreGraphics)
12-
public import Testing
13-
1411
@available(_uttypesAPI, *)
1512
extension Attachment {
1613
/// Initialize an instance of this type that encloses the given image.
@@ -52,12 +49,8 @@ extension Attachment {
5249
named preferredName: String? = nil,
5350
as imageFormat: AttachableImageFormat? = nil,
5451
sourceLocation: SourceLocation = #_sourceLocation
55-
) where T: AttachableAsCGImage, AttachableValue == _AttachableImageWrapper<T> {
56-
let imageWrapper = _AttachableImageWrapper(
57-
image: image._copyAttachableValue(),
58-
imageFormat: imageFormat,
59-
deinitializingWith: { _ in }
60-
)
52+
) where AttachableValue: _AttachableImageWrapper<T> & AttachableWrapper {
53+
let imageWrapper = AttachableValue(image: image, imageFormat: imageFormat)
6154
self.init(imageWrapper, named: preferredName, sourceLocation: sourceLocation)
6255
}
6356

@@ -99,7 +92,7 @@ extension Attachment {
9992
named preferredName: String? = nil,
10093
as imageFormat: AttachableImageFormat? = nil,
10194
sourceLocation: SourceLocation = #_sourceLocation
102-
) where T: AttachableAsCGImage, AttachableValue == _AttachableImageWrapper<T> {
95+
) where AttachableValue: _AttachableImageWrapper<T> & AttachableWrapper {
10396
let attachment = Self(image, named: preferredName, as: imageFormat, sourceLocation: sourceLocation)
10497
Self.record(attachment, sourceLocation: sourceLocation)
10598
}
@@ -109,11 +102,10 @@ extension Attachment {
109102

110103
@_spi(Experimental) // STOP: not part of ST-0014
111104
@available(_uttypesAPI, *)
112-
extension Attachment where AttachableValue: AttachableWrapper, AttachableValue.Wrapped: AttachableAsCGImage {
105+
extension Attachment where AttachableValue: AttachableWrapper, AttachableValue.Wrapped: _AttachableAsImage {
113106
/// The image format to use when encoding the represented image.
114107
@_disfavoredOverload public var imageFormat: AttachableImageFormat? {
115108
// FIXME: no way to express `where AttachableValue == _AttachableImageWrapper<???>` on a property (see rdar://47559973)
116109
(attachableValue as? _AttachableImageWrapper<AttachableValue.Wrapped>)?.imageFormat
117110
}
118111
}
119-
#endif
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2024–2025 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
//
10+
11+
/// A protocol describing images that can be converted to instances of
12+
/// [`Attachment`](https://developer.apple.com/documentation/testing/attachment).
13+
///
14+
/// This protocol acts as an abstract, platform-independent base protocol for
15+
/// ``AttachableAsCGImage`` and ``AttachableAsIWICBitmapSource``.
16+
///
17+
/// @Comment {
18+
/// A future Swift Evolution proposal will promote this protocol to API so
19+
/// that we don't need to underscore its name.
20+
/// }
21+
@available(_uttypesAPI, *)
22+
public protocol _AttachableAsImage: SendableMetatype {
23+
/// Make a copy of this instance to pass to an attachment.
24+
///
25+
/// - Returns: A copy of `self`, or `self` if no copy is needed.
26+
///
27+
/// The testing library uses this function to take ownership of image
28+
/// resources that test authors pass to it. If possible, make a copy of or add
29+
/// a reference to `self`. If this type does not support making copies, return
30+
/// `self` verbatim.
31+
///
32+
/// The default implementation of this function when `Self` conforms to
33+
/// `Sendable` simply returns `self`.
34+
///
35+
/// This function is not part of the public interface of the testing library.
36+
/// It may be removed in a future update.
37+
func _copyAttachableValue() -> Self
38+
39+
/// Manually deinitialize any resources associated with this image.
40+
///
41+
/// The implementation of this function cleans up any resources (such as
42+
/// handles or COM objects) associated with this image. The testing library
43+
/// automatically invokes this function as needed.
44+
///
45+
/// This function is not responsible for releasing the image returned from
46+
/// `_copyAttachableIWICBitmapSource(using:)`.
47+
///
48+
/// The default implementation of this function when `Self` conforms to
49+
/// `Sendable` does nothing.
50+
///
51+
/// This function is not part of the public interface of the testing library.
52+
/// It may be removed in a future update.
53+
func _deinitializeAttachableValue()
54+
}

Sources/Testing/Attachments/Images/_AttachableImageWrapper.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,20 @@
2121
/// | 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) |
2222
/// }
2323
@available(_uttypesAPI, *)
24-
public final class _AttachableImageWrapper<Image>: Sendable {
24+
public final class _AttachableImageWrapper<Image>: Sendable where Image: _AttachableAsImage {
2525
/// The underlying image.
2626
private nonisolated(unsafe) let _image: Image
2727

2828
/// The image format to use when encoding the represented image.
2929
package let imageFormat: AttachableImageFormat?
3030

31-
/// A deinitializer function to call to clean up `image`.
32-
private let _deinit: @Sendable (consuming Image) -> Void
33-
34-
package init(image: Image, imageFormat: AttachableImageFormat?, deinitializingWith `deinit`: @escaping @Sendable (consuming Image) -> Void) {
35-
self._image = image
31+
init(image: Image, imageFormat: AttachableImageFormat?) {
32+
self._image = image._copyAttachableValue()
3633
self.imageFormat = imageFormat
37-
self._deinit = `deinit`
3834
}
3935

4036
deinit {
41-
_deinit(_image)
37+
_image._deinitializeAttachableValue()
4238
}
4339
}
4440

Sources/Testing/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ add_library(Testing
2121
ABI/Encoded/ABI.EncodedIssue.swift
2222
ABI/Encoded/ABI.EncodedMessage.swift
2323
ABI/Encoded/ABI.EncodedTest.swift
24+
Attachments/Images/_AttachableAsImage.swift
2425
Attachments/Images/_AttachableImageWrapper.swift
2526
Attachments/Images/AttachableImageFormat.swift
27+
Attachments/Images/Attachment+_AttachableAsImage.swift
2628
Attachments/Images/ImageAttachmentError.swift
2729
Attachments/Attachable.swift
2830
Attachments/AttachableWrapper.swift

0 commit comments

Comments
 (0)