From 264f6f96866c8263ec971266578a81d51e067671 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 2 Jul 2025 13:58:02 -0400 Subject: [PATCH 1/6] Add a cross-import overlay with CoreImage to allow attaching `CIImage`s. This PR adds on to the Core Graphics cross-import overlay added in #827 to allow attaching instances of `CIImage` to a test. > [!NOTE] > Image attachments remain an experimental feature. --- Package.swift | 10 ++++++ .../Attachments/AttachableAsCGImage.swift | 1 + .../Attachment+AttachableAsCGImage.swift | 2 ++ .../Attachments/_AttachableImageWrapper.swift | 1 + .../CIImage+AttachableAsCGImage.swift | 31 +++++++++++++++++++ .../_Testing_CoreImage/ReexportTesting.swift | 11 +++++++ Tests/TestingTests/AttachmentTests.swift | 16 ++++++++++ 7 files changed, 72 insertions(+) create mode 100644 Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift create mode 100644 Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift diff --git a/Package.swift b/Package.swift index 5d08997e0..933e6153d 100644 --- a/Package.swift +++ b/Package.swift @@ -127,6 +127,7 @@ let package = Package( "Testing", "_Testing_AppKit", "_Testing_CoreGraphics", + "_Testing_CoreImage", "_Testing_Foundation", "MemorySafeTestingTests", ], @@ -208,6 +209,15 @@ let package = Package( path: "Sources/Overlays/_Testing_CoreGraphics", swiftSettings: .packageSettings + .enableLibraryEvolution() ), + .target( + name: "_Testing_CoreImage", + dependencies: [ + "Testing", + "_Testing_CoreGraphics", + ], + path: "Sources/Overlays/_Testing_CoreImage", + swiftSettings: .packageSettings + ), .target( name: "_Testing_Foundation", dependencies: [ diff --git a/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift b/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift index 08c94823c..a3d04e2c8 100644 --- a/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift +++ b/Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift @@ -24,6 +24,7 @@ private import ImageIO /// 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) /// diff --git a/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift b/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift index 6c9a76dd5..e2315197d 100644 --- a/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift +++ b/Sources/Overlays/_Testing_CoreGraphics/Attachments/Attachment+AttachableAsCGImage.swift @@ -36,6 +36,7 @@ extension Attachment { /// ``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) /// @@ -82,6 +83,7 @@ extension Attachment { /// ``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) /// diff --git a/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper.swift b/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper.swift index f61b17e7d..8c47e1c99 100644 --- a/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper.swift +++ b/Sources/Overlays/_Testing_CoreGraphics/Attachments/_AttachableImageWrapper.swift @@ -47,6 +47,7 @@ import UniformTypeIdentifiers /// 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) @_spi(Experimental) diff --git a/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift b/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift new file mode 100644 index 000000000..b066b3e3f --- /dev/null +++ b/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift @@ -0,0 +1,31 @@ +// +// 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(CoreImage) +public import CoreImage +@_spi(Experimental) public import _Testing_CoreGraphics + +@_spi(Experimental) +extension CIImage: AttachableAsCGImage { + public var attachableCGImage: CGImage { + get throws { + guard let result = CIContext().createCGImage(self, from: extent) else { + throw ImageAttachmentError.couldNotCreateCGImage + } + return result + } + } + + public func _makeCopyForAttachment() -> Self { + // CIImage is documented as thread-safe, but does not conform to Sendable. + self + } +} +#endif diff --git a/Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift b/Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift new file mode 100644 index 000000000..3faa622d7 --- /dev/null +++ b/Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift @@ -0,0 +1,11 @@ +// +// 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 +// + +@_exported public import Testing diff --git a/Tests/TestingTests/AttachmentTests.swift b/Tests/TestingTests/AttachmentTests.swift index 4b16d98ea..3a12cc68d 100644 --- a/Tests/TestingTests/AttachmentTests.swift +++ b/Tests/TestingTests/AttachmentTests.swift @@ -22,6 +22,10 @@ import _Testing_Foundation import CoreGraphics @_spi(Experimental) import _Testing_CoreGraphics #endif +#if canImport(CoreImage) +import CoreImage +@_spi(Experimental) import _Testing_CoreImage +#endif #if canImport(UniformTypeIdentifiers) import UniformTypeIdentifiers #endif @@ -582,6 +586,18 @@ extension AttachmentTests { } #endif +#if canImport(CoreImage) + @available(_uttypesAPI, *) + @Test func attachCIImage() throws { + let image = CIImage(cgImage: try Self.cgImage.get()) + let attachment = Attachment(image, named: "diamond.jpg") + #expect(attachment.attachableValue === image) + try attachment.attachableValue.withUnsafeBytes(for: attachment) { buffer in + #expect(buffer.count > 32) + } + } +#endif + #if canImport(AppKit) static var nsImage: NSImage { get throws { From dd53c00095c09ca20702735bb5a59474f072e3c4 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 2 Jul 2025 14:17:11 -0400 Subject: [PATCH 2/6] Update imports/reexports --- Sources/Overlays/_Testing_AppKit/ReexportTesting.swift | 4 ++-- Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/Overlays/_Testing_AppKit/ReexportTesting.swift b/Sources/Overlays/_Testing_AppKit/ReexportTesting.swift index 3716f1f01..ce80a70d9 100644 --- a/Sources/Overlays/_Testing_AppKit/ReexportTesting.swift +++ b/Sources/Overlays/_Testing_AppKit/ReexportTesting.swift @@ -8,5 +8,5 @@ // See https://swift.org/CONTRIBUTORS.txt for Swift project authors // -@_exported public import Testing -@_exported public import _Testing_CoreGraphics +@_exported @_spi(Experimental) @_spi(ForToolsIntegrationOnly) public import Testing +@_exported @_spi(Experimental) @_spi(ForToolsIntegrationOnly) public import _Testing_CoreGraphics diff --git a/Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift b/Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift index 3faa622d7..ce80a70d9 100644 --- a/Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift +++ b/Sources/Overlays/_Testing_CoreImage/ReexportTesting.swift @@ -8,4 +8,5 @@ // See https://swift.org/CONTRIBUTORS.txt for Swift project authors // -@_exported public import Testing +@_exported @_spi(Experimental) @_spi(ForToolsIntegrationOnly) public import Testing +@_exported @_spi(Experimental) @_spi(ForToolsIntegrationOnly) public import _Testing_CoreGraphics From 97f60fa222515c1470619a0da6c022b6d1796b24 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 2 Jul 2025 15:16:46 -0400 Subject: [PATCH 3/6] Add .enableLibraryEvolution() --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 933e6153d..5a8c4bf41 100644 --- a/Package.swift +++ b/Package.swift @@ -216,7 +216,7 @@ let package = Package( "_Testing_CoreGraphics", ], path: "Sources/Overlays/_Testing_CoreImage", - swiftSettings: .packageSettings + swiftSettings: .packageSettings + .enableLibraryEvolution() ), .target( name: "_Testing_Foundation", From 9e546a63603321d956866226197cdeef87bf9dc0 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 2 Jul 2025 17:14:07 -0400 Subject: [PATCH 4/6] Add to _Testing_ExperimentalImageAttachments --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index 8f1fb40ba..a6f7c6f36 100644 --- a/Package.swift +++ b/Package.swift @@ -90,6 +90,7 @@ let package = Package( targets: [ "_Testing_AppKit", "_Testing_CoreGraphics", + "_Testing_CoreImage", ] ) ] From 93e4f16d67f086b387ee6480f3c1fa53aaa631a5 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 9 Jul 2025 11:56:23 -0400 Subject: [PATCH 5/6] Use NSCopying if possible --- .../Attachments/CIImage+AttachableAsCGImage.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift b/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift index b066b3e3f..3485dfbab 100644 --- a/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift +++ b/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift @@ -25,7 +25,9 @@ extension CIImage: AttachableAsCGImage { public func _makeCopyForAttachment() -> Self { // CIImage is documented as thread-safe, but does not conform to Sendable. - self + // It conforms to NSCopying and does have mutable state, so we still want to + // make a (shallow) copy of it. + return self.copy() as? Self ?? self } } #endif From e973348b8cf95b06fa4a3a011d795d9e6ee39f2e Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 9 Jul 2025 13:04:29 -0400 Subject: [PATCH 6/6] Don't bother with NSCopying actually --- .../Attachments/CIImage+AttachableAsCGImage.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift b/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift index 3485dfbab..9a8278a83 100644 --- a/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift +++ b/Sources/Overlays/_Testing_CoreImage/Attachments/CIImage+AttachableAsCGImage.swift @@ -25,9 +25,9 @@ extension CIImage: AttachableAsCGImage { public func _makeCopyForAttachment() -> Self { // CIImage is documented as thread-safe, but does not conform to Sendable. - // It conforms to NSCopying and does have mutable state, so we still want to - // make a (shallow) copy of it. - return self.copy() as? Self ?? self + // It conforms to NSCopying but does not actually copy itself, so there's no + // point in calling copy(). + self } } #endif