-
Notifications
You must be signed in to change notification settings - Fork 111
Add a CoreGraphics cross-import overlay with support for attaching CGImage
s.
#827
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// | ||
// 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) | ||
public import CoreGraphics | ||
private import ImageIO | ||
|
||
/// A protocol describing images that can be converted to instances of | ||
/// ``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. | ||
/// | ||
/// The following system-provided image types conform to this protocol and can | ||
/// be attached to a test: | ||
/// | ||
/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) | ||
/// | ||
/// 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 AttachableAsCGImage { | ||
/// An instance of `CGImage` representing this image. | ||
/// | ||
/// - Throws: Any error that prevents the creation of an image. | ||
var attachableCGImage: CGImage { get throws } | ||
|
||
/// The orientation of the image. | ||
/// | ||
/// The value of this property is the raw value of an instance of | ||
/// `CGImagePropertyOrientation`. The default value of this property is | ||
/// `.up`. | ||
/// | ||
/// This property is not part of the public interface of the testing | ||
/// library. It may be removed in a future update. | ||
var _attachmentOrientation: UInt32 { get } | ||
|
||
/// The scale factor of the image. | ||
/// | ||
/// The value of this property is typically greater than `1.0` when an image | ||
/// originates from a Retina Display screenshot or similar. The default value | ||
/// of this property is `1.0`. | ||
/// | ||
/// This property is not part of the public interface of the testing | ||
/// library. It may be removed in a future update. | ||
var _attachmentScaleFactor: CGFloat { get } | ||
|
||
/// Make a copy of this instance to pass to an attachment. | ||
/// | ||
/// - Returns: A copy of `self`, or `self` if no copy is needed. | ||
/// | ||
/// Several system image types do not conform to `Sendable`; use this | ||
/// function to make copies of such images that will not be shared outside | ||
/// of an attachment and so can be generally safely stored. | ||
/// | ||
/// The default implementation of this function when `Self` conforms to | ||
/// `Sendable` simply returns `self`. | ||
/// | ||
/// This function is not part of the public interface of the testing library. | ||
/// It may be removed in a future update. | ||
func _makeCopyForAttachment() -> Self | ||
} | ||
|
||
extension AttachableAsCGImage { | ||
public var _attachmentOrientation: UInt32 { | ||
CGImagePropertyOrientation.up.rawValue | ||
} | ||
|
||
public var _attachmentScaleFactor: CGFloat { | ||
1.0 | ||
} | ||
} | ||
|
||
extension AttachableAsCGImage where Self: Sendable { | ||
public func _makeCopyForAttachment() -> Self { | ||
self | ||
} | ||
} | ||
#endif |
147 changes: 147 additions & 0 deletions
147
Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// | ||
// 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) | ||
@_spi(ForSwiftTestingOnly) @_spi(Experimental) public import Testing | ||
|
||
public import UniformTypeIdentifiers | ||
|
||
extension Attachment { | ||
/// Initialize an instance of this type that encloses the given image. | ||
/// | ||
/// - Parameters: | ||
/// - attachableValue: The value that will be attached to the output of | ||
/// the test run. | ||
/// - preferredName: The preferred name of the attachment when writing it | ||
/// to a test report or to disk. If `nil`, the testing library attempts | ||
/// to derive a reasonable filename for the attached value. | ||
/// - contentType: The image format with which to encode `attachableValue`. | ||
/// If this type does not conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image), | ||
/// the result is undefined. Pass `nil` to let the testing library decide | ||
/// which image format to use. | ||
/// - encodingQuality: The encoding quality to use when encoding the image. | ||
/// If the image format used for encoding (specified by the `contentType` | ||
/// argument) does not support variable-quality encoding, the value of | ||
/// this argument is ignored. | ||
/// - sourceLocation: The source location of the call to this initializer. | ||
/// This value is used when recording issues associated with the | ||
/// attachment. | ||
/// | ||
/// This is the designated initializer for this type when attaching an image | ||
/// that conforms to ``AttachableAsCGImage``. | ||
fileprivate init<T>( | ||
attachableValue: T, | ||
named preferredName: String?, | ||
contentType: (any Sendable)?, | ||
encodingQuality: Float, | ||
sourceLocation: SourceLocation | ||
) where AttachableValue == _AttachableImageContainer<T> { | ||
var imageContainer = _AttachableImageContainer(image: attachableValue, encodingQuality: encodingQuality) | ||
|
||
// Update the preferred name to include an extension appropriate for the | ||
// given content type. (Note the `else` branch duplicates the logic in | ||
// `preferredContentType(forEncodingQuality:)` but will go away once our | ||
// minimum deployment targets include the UniformTypeIdentifiers framework.) | ||
var preferredName = preferredName ?? Self.defaultPreferredName | ||
if #available(_uttypesAPI, *) { | ||
let contentType: UTType = contentType | ||
.map { $0 as! UTType } | ||
.flatMap { contentType in | ||
if UTType.image.conforms(to: contentType) { | ||
// This type is an abstract base type of .image (or .image itself.) | ||
// We'll infer the concrete type based on other arguments. | ||
return nil | ||
} | ||
return contentType | ||
} ?? .preferred(forEncodingQuality: encodingQuality) | ||
preferredName = (preferredName as NSString).appendingPathExtension(for: contentType) | ||
imageContainer.contentType = contentType | ||
} else { | ||
// The caller can't provide a content type, so we'll pick one for them. | ||
let ext = if encodingQuality < 1.0 { | ||
"jpg" | ||
} else { | ||
"png" | ||
} | ||
if (preferredName as NSString).pathExtension.caseInsensitiveCompare(ext) != .orderedSame { | ||
preferredName = (preferredName as NSString).appendingPathExtension(ext) ?? preferredName | ||
} | ||
} | ||
|
||
self.init(imageContainer, named: preferredName, sourceLocation: sourceLocation) | ||
} | ||
|
||
/// Initialize an instance of this type that encloses the given image. | ||
/// | ||
/// - Parameters: | ||
/// - attachableValue: The value that will be attached to the output of | ||
/// the test run. | ||
/// - preferredName: The preferred name of the attachment when writing it | ||
/// to a test report or to disk. If `nil`, the testing library attempts | ||
/// to derive a reasonable filename for the attached value. | ||
/// - contentType: The image format with which to encode `attachableValue`. | ||
/// If this type does not conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image), | ||
/// the result is undefined. Pass `nil` to let the testing library decide | ||
/// which image format to use. | ||
/// - encodingQuality: The encoding quality to use when encoding the image. | ||
/// If the image format used for encoding (specified by the `contentType` | ||
/// argument) does not support variable-quality encoding, the value of | ||
/// this argument is ignored. | ||
/// - sourceLocation: The source location of the call to this initializer. | ||
/// 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: | ||
/// | ||
/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) | ||
@_spi(Experimental) | ||
@available(_uttypesAPI, *) | ||
public init<T>( | ||
_ attachableValue: T, | ||
named preferredName: String? = nil, | ||
as contentType: UTType?, | ||
grynspan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
encodingQuality: Float = 1.0, | ||
sourceLocation: SourceLocation = #_sourceLocation | ||
) where AttachableValue == _AttachableImageContainer<T> { | ||
self.init(attachableValue: attachableValue, named: preferredName, contentType: contentType, encodingQuality: encodingQuality, sourceLocation: sourceLocation) | ||
} | ||
|
||
/// Initialize an instance of this type that encloses the given image. | ||
/// | ||
/// - Parameters: | ||
/// - attachableValue: The value that will be attached to the output of | ||
/// the test run. | ||
/// - preferredName: The preferred name of the attachment when writing it | ||
/// to a test report or to disk. If `nil`, the testing library attempts | ||
/// to derive a reasonable filename for the attached value. | ||
/// - encodingQuality: The encoding quality to use when encoding the image. | ||
/// If the image format used for encoding (specified by the `contentType` | ||
/// argument) does not support variable-quality encoding, the value of | ||
/// this argument is ignored. | ||
/// - sourceLocation: The source location of the call to this initializer. | ||
/// 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: | ||
/// | ||
/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) | ||
@_spi(Experimental) | ||
public init<T>( | ||
_ attachableValue: T, | ||
named preferredName: String? = nil, | ||
encodingQuality: Float = 1.0, | ||
sourceLocation: SourceLocation = #_sourceLocation | ||
) where AttachableValue == _AttachableImageContainer<T> { | ||
self.init(attachableValue: attachableValue, named: preferredName, contentType: nil, encodingQuality: encodingQuality, sourceLocation: sourceLocation) | ||
} | ||
} | ||
#endif |
20 changes: 20 additions & 0 deletions
20
Sources/Overlays/_Testing_CoreGraphics/Attachments/CGImage+AttachableAsCGImage.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// 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) | ||
public import CoreGraphics | ||
|
||
@_spi(Experimental) | ||
extension CGImage: AttachableAsCGImage { | ||
public var attachableCGImage: CGImage { | ||
self | ||
} | ||
} | ||
#endif |
41 changes: 41 additions & 0 deletions
41
Sources/Overlays/_Testing_CoreGraphics/Attachments/ImageAttachmentError.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// | ||
// 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. | ||
@_spi(ForSwiftTestingOnly) | ||
public enum ImageAttachmentError: Error, CustomStringConvertible { | ||
/// The specified content type did not conform to `.image`. | ||
case contentTypeDoesNotConformToImage | ||
|
||
/// 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 | ||
|
||
@_spi(ForSwiftTestingOnly) | ||
public var description: String { | ||
switch self { | ||
case .contentTypeDoesNotConformToImage: | ||
"The specified type does not represent an image format." | ||
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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.