Skip to content

Commit 87425d9

Browse files
authored
Merge branch 'swiftlang:main' into feature/advanced-console-output-recorder
2 parents 73a60ae + 880c37b commit 87425d9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+847
-373
lines changed

CONTRIBUTING.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,10 @@ and install a toolchain.
4848

4949
#### Installing a toolchain
5050

51-
1. Download a toolchain. A recent **6.0 development snapshot** toolchain is
52-
required to build the testing library. Visit
53-
[swift.org](http://swift.org/install) and download the most recent toolchain
54-
from the section titled **release/6.0** under **Development Snapshots** on
55-
the page for your platform.
51+
1. Download a toolchain. A recent **development snapshot** toolchain is required
52+
to build the testing library. Visit [swift.org](https://swift.org/install),
53+
select your platform, and download the most recent toolchain from the section
54+
titled **release/6.x** under **Development Snapshots**.
5655

5756
Be aware that development snapshot toolchains aren't intended for day-to-day
5857
development and may contain defects that affect the programs built with them.

Package.swift

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ let package = Package(
9090
targets: [
9191
"_Testing_AppKit",
9292
"_Testing_CoreGraphics",
93+
"_Testing_CoreImage",
94+
"_Testing_UIKit",
9395
]
9496
)
9597
]
@@ -105,13 +107,6 @@ let package = Package(
105107
return result
106108
}(),
107109

108-
traits: [
109-
.trait(
110-
name: "ExperimentalExitTestValueCapture",
111-
description: "Enable experimental support for capturing values in exit tests"
112-
),
113-
],
114-
115110
dependencies: [
116111
.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "602.0.0-latest"),
117112
],
@@ -137,7 +132,9 @@ let package = Package(
137132
"Testing",
138133
"_Testing_AppKit",
139134
"_Testing_CoreGraphics",
135+
"_Testing_CoreImage",
140136
"_Testing_Foundation",
137+
"_Testing_UIKit",
141138
"MemorySafeTestingTests",
142139
],
143140
swiftSettings: .packageSettings
@@ -218,6 +215,15 @@ let package = Package(
218215
path: "Sources/Overlays/_Testing_CoreGraphics",
219216
swiftSettings: .packageSettings + .enableLibraryEvolution()
220217
),
218+
.target(
219+
name: "_Testing_CoreImage",
220+
dependencies: [
221+
"Testing",
222+
"_Testing_CoreGraphics",
223+
],
224+
path: "Sources/Overlays/_Testing_CoreImage",
225+
swiftSettings: .packageSettings + .enableLibraryEvolution()
226+
),
221227
.target(
222228
name: "_Testing_Foundation",
223229
dependencies: [
@@ -230,6 +236,16 @@ let package = Package(
230236
// it can only enable Library Evolution itself on those platforms.
231237
swiftSettings: .packageSettings + .enableLibraryEvolution(.whenApple())
232238
),
239+
.target(
240+
name: "_Testing_UIKit",
241+
dependencies: [
242+
"Testing",
243+
"_Testing_CoreGraphics",
244+
"_Testing_CoreImage",
245+
],
246+
path: "Sources/Overlays/_Testing_UIKit",
247+
swiftSettings: .packageSettings + .enableLibraryEvolution()
248+
),
233249

234250
// Utility targets: These are utilities intended for use when developing
235251
// this package, not for distribution.
@@ -360,14 +376,6 @@ extension Array where Element == PackageDescription.SwiftSetting {
360376
.define("SWT_NO_LIBDISPATCH", .whenEmbedded()),
361377
]
362378

363-
// Unconditionally enable 'ExperimentalExitTestValueCapture' when building
364-
// for development.
365-
if buildingForDevelopment {
366-
result += [
367-
.define("ExperimentalExitTestValueCapture")
368-
]
369-
}
370-
371379
return result
372380
}
373381

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Swift expressions and operators, and captures the evaluated values so you can
2323
quickly understand what went wrong when a test fails.
2424

2525
```swift
26+
import Testing
27+
2628
@Test func helloWorld() {
2729
let greeting = "Hello, world!"
2830
#expect(greeting == "Hello") // Expectation failed: (greeting → "Hello, world!") == "Hello"
@@ -84,11 +86,19 @@ func mentionedContinents(videoName: String) async throws {
8486

8587
### Cross-platform support
8688

87-
Swift Testing works on all major platforms supported by Swift, including Apple
88-
platforms, Linux, and Windows, so your tests can behave more consistently when
89-
moving between platforms. It’s developed as open source and discussed on the
90-
[Swift Forums](https://forums.swift.org/c/development/swift-testing/103) so the
91-
very best ideas, from anywhere, can help shape the future of testing in Swift.
89+
Swift Testing is included in officially-supported Swift toolchains, including
90+
those for Apple platforms, Linux, and Windows. To use the library, import the
91+
`Testing` module:
92+
93+
```swift
94+
import Testing
95+
```
96+
97+
You don't need to declare a package dependency to use Swift Testing. It's
98+
developed as open source and discussed on the
99+
[Swift Forums](https://forums.swift.org/c/development/swift-testing/103)
100+
so the very best ideas, from anywhere, can help shape the future of testing in
101+
Swift.
92102

93103
The table below describes the current level of support that Swift Testing has
94104
for various platforms:
@@ -101,6 +111,8 @@ for various platforms:
101111
| **tvOS** | | | Supported |
102112
| **visionOS** | | | Supported |
103113
| **Ubuntu 22.04** | [![Build Status](https://ci.swift.org/buildStatus/icon?job=swift-testing-main-swift-6.1-linux)](https://ci.swift.org/job/swift-testing-main-swift-6.1-linux/) | [![Build Status](https://ci.swift.org/buildStatus/icon?job=swift-testing-main-swift-main-linux)](https://ci.swift.org/view/Swift%20Packages/job/swift-testing-main-swift-main-linux/) | Supported |
114+
| **FreeBSD** | | | Experimental |
115+
| **OpenBSD** | | | Experimental |
104116
| **Windows** | [![Build Status](https://ci-external.swift.org/buildStatus/icon?job=swift-testing-main-swift-6.1-windows)](https://ci-external.swift.org/view/all/job/swift-testing-main-swift-6.1-windows/) | [![Build Status](https://ci-external.swift.org/buildStatus/icon?job=swift-testing-main-swift-main-windows)](https://ci-external.swift.org/job/swift-testing-main-swift-main-windows/) | Supported |
105117
| **Wasm** | | | Experimental |
106118

Sources/Overlays/_Testing_AppKit/Attachments/NSImage+AttachableAsCGImage.swift

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@
1212
public import AppKit
1313
@_spi(Experimental) public import _Testing_CoreGraphics
1414

15+
extension NSImageRep {
16+
/// AppKit's bundle.
17+
private static let _appKitBundle: Bundle = Bundle(for: NSImageRep.self)
18+
19+
/// Whether or not this image rep's class is effectively thread-safe and can
20+
/// be treated as if it conforms to `Sendable`.
21+
fileprivate var isEffectivelySendable: Bool {
22+
if isMember(of: NSImageRep.self) || isKind(of: NSCustomImageRep.self) {
23+
// NSImageRep itself is an abstract class. NSCustomImageRep includes an
24+
// arbitrary rendering block that may not be concurrency-safe in Swift.
25+
return false
26+
}
27+
28+
// Treat all other classes declared in AppKit as safe. We can't reason about
29+
// classes declared in other bundles, so treat them all as if they're unsafe.
30+
return Bundle(for: Self.self) == Self._appKitBundle
31+
}
32+
}
33+
34+
// MARK: -
35+
1536
@_spi(Experimental)
1637
extension NSImage: AttachableAsCGImage {
1738
public var attachableCGImage: CGImage {
@@ -32,29 +53,6 @@ extension NSImage: AttachableAsCGImage {
3253
return maxRepWidth ?? 1.0
3354
}
3455

35-
/// Get the base address of the loaded image containing `class`.
36-
///
37-
/// - Parameters:
38-
/// - class: The class to look for.
39-
///
40-
/// - Returns: The base address of the image containing `class`, or `nil` if
41-
/// no image was found (for instance, if the class is generic or dynamically
42-
/// generated.)
43-
///
44-
/// "Image" in this context refers to a binary/executable image.
45-
private static func _baseAddressOfImage(containing `class`: AnyClass) -> UnsafeRawPointer? {
46-
let classAsAddress = Unmanaged.passUnretained(`class` as AnyObject).toOpaque()
47-
48-
var info = Dl_info()
49-
guard 0 != dladdr(classAsAddress, &info) else {
50-
return nil
51-
}
52-
return .init(info.dli_fbase)
53-
}
54-
55-
/// The base address of the image containing AppKit's symbols, if known.
56-
private static nonisolated(unsafe) let _appKitBaseAddress = _baseAddressOfImage(containing: NSImageRep.self)
57-
5856
public func _makeCopyForAttachment() -> Self {
5957
// If this image is of an NSImage subclass, we cannot reliably make a deep
6058
// copy of it because we don't know what its `init(data:)` implementation
@@ -69,18 +67,7 @@ extension NSImage: AttachableAsCGImage {
6967

7068
// Check whether the image contains any representations that we don't think
7169
// are safe. If it does, then make a "safe" copy.
72-
let allImageRepsAreSafe = representations.allSatisfy { imageRep in
73-
// NSCustomImageRep includes an arbitrary rendering block that may not be
74-
// concurrency-safe in Swift.
75-
if imageRep is NSCustomImageRep {
76-
return false
77-
}
78-
79-
// Treat all other classes declared in AppKit as safe. We can't reason
80-
// about classes declared in other modules, so treat them all as if they
81-
// are unsafe.
82-
return Self._baseAddressOfImage(containing: type(of: imageRep)) == Self._appKitBaseAddress
83-
}
70+
let allImageRepsAreSafe = representations.allSatisfy(\.isEffectivelySendable)
8471
if !allImageRepsAreSafe, let safeCopy = tiffRepresentation.flatMap(Self.init(data:)) {
8572
// Create a "safe" copy of this image by flattening it to TIFF and then
8673
// creating a new NSImage instance from it.

Sources/Overlays/_Testing_AppKit/ReexportTesting.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11-
@_exported public import Testing
12-
@_exported public import _Testing_CoreGraphics
11+
@_exported @_spi(Experimental) @_spi(ForToolsIntegrationOnly) public import Testing
12+
@_exported @_spi(Experimental) @_spi(ForToolsIntegrationOnly) public import _Testing_CoreGraphics

Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableAsCGImage.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ private import ImageIO
2424
/// be attached to a test:
2525
///
2626
/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage)
27+
/// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage)
2728
/// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage)
2829
/// (macOS)
30+
/// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage)
31+
/// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst)
2932
///
3033
/// You do not generally need to add your own conformances to this protocol. If
3134
/// you have an image in another format that needs to be attached to a test,
3235
/// first convert it to an instance of one of the types above.
3336
@_spi(Experimental)
37+
@available(_uttypesAPI, *)
3438
public protocol AttachableAsCGImage {
3539
/// An instance of `CGImage` representing this image.
3640
///
@@ -73,6 +77,7 @@ public protocol AttachableAsCGImage {
7377
func _makeCopyForAttachment() -> Self
7478
}
7579

80+
@available(_uttypesAPI, *)
7681
extension AttachableAsCGImage {
7782
public var _attachmentOrientation: UInt32 {
7883
CGImagePropertyOrientation.up.rawValue
@@ -83,6 +88,7 @@ extension AttachableAsCGImage {
8388
}
8489
}
8590

91+
@available(_uttypesAPI, *)
8692
extension AttachableAsCGImage where Self: Sendable {
8793
public func _makeCopyForAttachment() -> Self {
8894
self
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
#if SWT_TARGET_OS_APPLE && canImport(CoreGraphics)
12+
@_spi(Experimental) public import Testing
13+
14+
public import UniformTypeIdentifiers
15+
16+
@available(_uttypesAPI, *)
17+
extension AttachableImageFormat {
18+
/// Get the content type to use when encoding the image, substituting a
19+
/// concrete type for `UTType.image` in particular.
20+
///
21+
/// - Parameters:
22+
/// - imageFormat: The image format to use, or `nil` if the developer did
23+
/// not specify one.
24+
/// - preferredName: The preferred name of the image for which a type is
25+
/// needed.
26+
///
27+
/// - Returns: An instance of `UTType` referring to a concrete image type.
28+
///
29+
/// This function is not part of the public interface of the testing library.
30+
static func computeContentType(for imageFormat: Self?, withPreferredName preferredName: String) -> UTType {
31+
guard let imageFormat else {
32+
// The developer didn't specify a type. Substitute the generic `.image`
33+
// and solve for that instead.
34+
return computeContentType(for: Self(.image, encodingQuality: 1.0), withPreferredName: preferredName)
35+
}
36+
37+
switch imageFormat.kind {
38+
case .png:
39+
return .png
40+
case .jpeg:
41+
return .jpeg
42+
case let .systemValue(contentType):
43+
let contentType = contentType as! UTType
44+
if contentType != .image {
45+
// The developer explicitly specified a type.
46+
return contentType
47+
}
48+
49+
// The developer didn't specify a concrete type, so try to derive one from
50+
// the preferred name's path extension.
51+
let pathExtension = (preferredName as NSString).pathExtension
52+
if !pathExtension.isEmpty,
53+
let contentType = UTType(filenameExtension: pathExtension, conformingTo: .image),
54+
contentType.isDeclared {
55+
return contentType
56+
}
57+
58+
// We couldn't derive a concrete type from the path extension, so pick
59+
// between PNG and JPEG based on the encoding quality.
60+
return imageFormat.encodingQuality < 1.0 ? .jpeg : .png
61+
}
62+
}
63+
64+
/// The content type corresponding to this image format.
65+
///
66+
/// The value of this property always conforms to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image).
67+
public var contentType: UTType {
68+
switch kind {
69+
case .png:
70+
return .png
71+
case .jpeg:
72+
return .jpeg
73+
case let .systemValue(contentType):
74+
return contentType as! UTType
75+
}
76+
}
77+
78+
/// Initialize an instance of this type with the given content type and
79+
/// encoding quality.
80+
///
81+
/// - Parameters:
82+
/// - contentType: The image format to use when encoding images.
83+
/// - encodingQuality: The encoding quality to use when encoding images. For
84+
/// the lowest supported quality, pass `0.0`. For the highest supported
85+
/// quality, pass `1.0`.
86+
///
87+
/// If the target image format does not support variable-quality encoding,
88+
/// the value of the `encodingQuality` argument is ignored.
89+
///
90+
/// If `contentType` does not conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image),
91+
/// the result is undefined.
92+
public init(_ contentType: UTType, encodingQuality: Float = 1.0) {
93+
precondition(
94+
contentType.conforms(to: .image),
95+
"An image cannot be attached as an instance of type '\(contentType.identifier)'. Use a type that conforms to 'public.image' instead."
96+
)
97+
self.init(kind: .systemValue(contentType), encodingQuality: encodingQuality)
98+
}
99+
}
100+
#endif

0 commit comments

Comments
 (0)