Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ let package = Package(
name: "SymbolShowcase",
dependencies: [
"Testing",
"_Testing_CoreGraphics",
],
swiftSettings: .packageSettings
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

public import UniformTypeIdentifiers

@_spi(Experimental)
@available(_uttypesAPI, *)
extension Attachment {
/// Initialize an instance of this type that encloses the given image.
///
Expand All @@ -23,46 +25,9 @@ extension Attachment {
/// 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 == _AttachableImageWrapper<T> {
let imageWrapper = _AttachableImageWrapper(image: attachableValue, encodingQuality: encodingQuality, contentType: contentType)
self.init(imageWrapper, 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.
/// For the lowest supported quality, pass `0.0`. For the highest
/// supported quality, pass `1.0`.
/// - sourceLocation: The source location of the call to this initializer.
/// This value is used when recording issues associated with the
/// attachment.
Expand All @@ -71,46 +36,72 @@ extension Attachment {
/// ``AttachableAsCGImage`` protocol and can be attached to a test:
///
/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage)
@_spi(Experimental)
@available(_uttypesAPI, *)
///
/// The testing library uses the image format specified by `contentType`. Pass
/// `nil` to let the testing library decide which image format to use. If you
/// pass `nil`, then the image format that the testing library uses depends on
/// the path extension you specify in `preferredName`, if any. If you do not
/// specify a path extension, or if the path extension you specify doesn't
/// correspond to an image format the operating system knows how to write, the
/// testing library selects an appropriate image format for you.
///
/// If the target image format does not support variable-quality encoding,
/// the value of the `encodingQuality` argument is ignored. If `contentType`
/// is not `nil` and does not conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image),
/// the result is undefined.
public init<T>(
_ attachableValue: T,
named preferredName: String? = nil,
as contentType: UTType?,
as contentType: UTType? = nil,
encodingQuality: Float = 1.0,
sourceLocation: SourceLocation = #_sourceLocation
) where AttachableValue == _AttachableImageWrapper<T> {
self.init(attachableValue: attachableValue, named: preferredName, contentType: contentType, encodingQuality: encodingQuality, sourceLocation: sourceLocation)
let imageWrapper = _AttachableImageWrapper(image: attachableValue, encodingQuality: encodingQuality, contentType: contentType)
self.init(imageWrapper, named: preferredName, sourceLocation: sourceLocation)
}

/// Initialize an instance of this type that encloses the given image.
/// Attach an image to the current test.
///
/// - 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.
/// - image: The value to attach.
/// - 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`.
/// - 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.
/// For the lowest supported quality, pass `0.0`. For the highest
/// supported quality, pass `1.0`.
/// - 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.
///
/// 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,
///
/// The testing library uses the image format specified by `contentType`. Pass
/// `nil` to let the testing library decide which image format to use. If you
/// pass `nil`, then the image format that the testing library uses depends on
/// the path extension you specify in `preferredName`, if any. If you do not
/// specify a path extension, or if the path extension you specify doesn't
/// correspond to an image format the operating system knows how to write, the
/// testing library selects an appropriate image format for you.
///
/// If the target image format does not support variable-quality encoding,
/// the value of the `encodingQuality` argument is ignored. If `contentType`
/// is not `nil` and does not conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image),
/// the result is undefined.
public static func record<T>(
_ image: consuming T,
named preferredName: String? = nil,
as contentType: UTType? = nil,
encodingQuality: Float = 1.0,
sourceLocation: SourceLocation = #_sourceLocation
) where AttachableValue == _AttachableImageWrapper<T> {
self.init(attachableValue: attachableValue, named: preferredName, contentType: nil, encodingQuality: encodingQuality, sourceLocation: sourceLocation)
let attachment = Self(image, named: preferredName, as: contentType, encodingQuality: encodingQuality, sourceLocation: sourceLocation)
Self.record(attachment, sourceLocation: sourceLocation)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import UniformTypeIdentifiers
///
/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage)
@_spi(Experimental)
@available(_uttypesAPI, *)
public struct _AttachableImageWrapper<Image>: Sendable where Image: AttachableAsCGImage {
/// The underlying image.
///
Expand All @@ -61,7 +62,7 @@ public struct _AttachableImageWrapper<Image>: Sendable where Image: AttachableAs
var encodingQuality: Float

/// Storage for ``contentType``.
private var _contentType: (any Sendable)?
private var _contentType: UTType?

/// The content type to use when encoding the image.
///
Expand All @@ -70,14 +71,9 @@ public struct _AttachableImageWrapper<Image>: Sendable where Image: AttachableAs
///
/// If the value of this property does not conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image),
/// the result is undefined.
@available(_uttypesAPI, *)
var contentType: UTType {
get {
if let contentType = _contentType as? UTType {
return contentType
} else {
return encodingQuality < 1.0 ? .jpeg : .png
}
_contentType ?? (encodingQuality < 1.0 ? .jpeg : .png)
}
set {
precondition(
Expand All @@ -92,9 +88,8 @@ public struct _AttachableImageWrapper<Image>: Sendable where Image: AttachableAs
/// type for `UTType.image`.
///
/// This property is not part of the public interface of the testing library.
@available(_uttypesAPI, *)
var computedContentType: UTType {
if let contentType = _contentType as? UTType, contentType != .image {
if let contentType = _contentType, contentType != .image {
contentType
} else {
encodingQuality < 1.0 ? .jpeg : .png
Expand All @@ -109,24 +104,21 @@ public struct _AttachableImageWrapper<Image>: Sendable where Image: AttachableAs
/// This property is not part of the public interface of the testing library.
/// It is used by ImageIO below.
var typeIdentifier: CFString {
if #available(_uttypesAPI, *) {
computedContentType.identifier as CFString
} else {
encodingQuality < 1.0 ? kUTTypeJPEG : kUTTypePNG
}
computedContentType.identifier as CFString
}

init(image: Image, encodingQuality: Float, contentType: (any Sendable)?) {
init(image: Image, encodingQuality: Float, contentType: UTType?) {
self.image = image._makeCopyForAttachment()
self.encodingQuality = encodingQuality
if #available(_uttypesAPI, *), let contentType = contentType as? UTType {
if let contentType {
self.contentType = contentType
}
}
}

// MARK: -

@available(_uttypesAPI, *)
extension _AttachableImageWrapper: AttachableWrapper {
public var wrappedValue: Image {
image
Expand Down Expand Up @@ -168,11 +160,7 @@ extension _AttachableImageWrapper: AttachableWrapper {
}

public borrowing func preferredName(for attachment: borrowing Attachment<Self>, basedOn suggestedName: String) -> String {
if #available(_uttypesAPI, *) {
return (suggestedName as NSString).appendingPathExtension(for: computedContentType)
}

return suggestedName
(suggestedName as NSString).appendingPathExtension(for: computedContentType)
}
}
#endif
17 changes: 17 additions & 0 deletions Tests/TestingTests/AttachmentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,23 @@ extension AttachmentTests {
Attachment.record(attachment)
}

@available(_uttypesAPI, *)
@Test func attachCGImageDirectly() async throws {
await confirmation("Attachment detected") { valueAttached in
var configuration = Configuration()
configuration.eventHandler = { event, _ in
if case .valueAttached = event.kind {
valueAttached()
}
}

await Test {
let image = try Self.cgImage.get()
Attachment.record(image, named: "diamond.jpg")
}.run(configuration: configuration)
}
}

@available(_uttypesAPI, *)
@Test(arguments: [Float(0.0).nextUp, 0.25, 0.5, 0.75, 1.0], [.png as UTType?, .jpeg, .gif, .image, nil])
func attachCGImage(quality: Float, type: UTType?) throws {
Expand Down