Skip to content

Commit 7895995

Browse files
authored
Remove the Confirmation.ExpectedCount marker protocol. (#689)
This PR replaces uses of `Confirmation.ExpectedCount` in our API surface with `RangeExpression<Int> & Sendable`. A composed protocol type `P<A> & Q` can only be expressed as `some`, not `any` as the latter is not yet implemented in the compiler (rdar://96960993), so we were using a separate protocol in place of that combination. The downside of doing so is that it makes the legibility of the interface worse. Xcode and other IDEs will offer you `confirmation(expectedCount: Confirmation.ExpectedCount)` but don't tell you that you need to write some range expression such as `0...2`. You have to dig into the protocol definition/documentation to figure it out. So this change changes the signature of the experimental `confirmation()` overload to take a `some RangeExpression<Int> & Sendable` and plumbs that through everywhere it's valid. Once we get to generating an `Issue`, we need an existential box (`any`) at which point we will just drop the `<Int>` bit until the compiler feature is added. We have a parameterized unit test that takes an array of these values, so to keep it building and passing, I moved a copy of the `ExpectedCount` protocol to the fixtures section of the test file containing that test; it's present only in our test target and not in our API surface. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent fc35539 commit 7895995

File tree

3 files changed

+25
-37
lines changed

3 files changed

+25
-37
lines changed

Sources/Testing/Issues/Confirmation.swift

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ public func confirmation<R>(
164164
@_spi(Experimental)
165165
public func confirmation<R>(
166166
_ comment: Comment? = nil,
167-
expectedCount: some Confirmation.ExpectedCount,
167+
expectedCount: some RangeExpression<Int> & Sendable,
168168
isolation: isolated (any Actor)? = #isolation,
169169
sourceLocation: SourceLocation = #_sourceLocation,
170170
_ body: (Confirmation) async throws -> sending R
@@ -200,22 +200,7 @@ public func confirmation<R>(
200200
fatalError("Unsupported")
201201
}
202202

203-
@_spi(Experimental)
204-
extension Confirmation {
205-
/// A protocol that describes a range expression that can be used with
206-
/// ``confirmation(_:expectedCount:isolation:sourceLocation:_:)-9rt6m``.
207-
///
208-
/// This protocol represents any expression that describes a range of
209-
/// confirmation counts. For example, the expression `1 ..< 10` automatically
210-
/// conforms to it.
211-
///
212-
/// You do not generally need to add conformances to this type yourself. It is
213-
/// used by the testing library to abstract away the different range types
214-
/// provided by the Swift standard library.
215-
public protocol ExpectedCount: Sendable, RangeExpression<Int> {}
216-
}
217-
218-
extension Confirmation.ExpectedCount {
203+
extension RangeExpression where Bound == Int, Self: Sendable {
219204
/// Get an instance of ``Issue/Kind-swift.enum`` corresponding to this value.
220205
///
221206
/// - Parameters:
@@ -233,18 +218,3 @@ extension Confirmation.ExpectedCount {
233218
}
234219
}
235220
}
236-
237-
@_spi(Experimental)
238-
extension ClosedRange<Int>: Confirmation.ExpectedCount {}
239-
240-
@_spi(Experimental)
241-
extension PartialRangeFrom<Int>: Confirmation.ExpectedCount {}
242-
243-
@_spi(Experimental)
244-
extension PartialRangeThrough<Int>: Confirmation.ExpectedCount {}
245-
246-
@_spi(Experimental)
247-
extension PartialRangeUpTo<Int>: Confirmation.ExpectedCount {}
248-
249-
@_spi(Experimental)
250-
extension Range<Int>: Confirmation.ExpectedCount {}

Sources/Testing/Issues/Issue.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public struct Issue: Sendable {
5252
/// the confirmation passed to these functions' `body` closures is confirmed
5353
/// too few or too many times.
5454
@_spi(Experimental)
55-
indirect case confirmationOutOfRange(actual: Int, expected: any Confirmation.ExpectedCount)
55+
indirect case confirmationOutOfRange(actual: Int, expected: any RangeExpression & Sendable)
5656

5757
/// An issue due to an `Error` being thrown by a test function and caught by
5858
/// the testing library.
@@ -246,8 +246,14 @@ extension Issue {
246246
///
247247
/// - Parameter issue: The original issue that gets snapshotted.
248248
public init(snapshotting issue: borrowing Issue) {
249-
self.kind = Issue.Kind.Snapshot(snapshotting: issue.kind)
250-
self.comments = issue.comments
249+
if case .confirmationOutOfRange = issue.kind {
250+
// Work around poor stringification of this issue kind in Xcode 16.
251+
self.kind = .unconditional
252+
self.comments = CollectionOfOne("\(issue.kind)") + issue.comments
253+
} else {
254+
self.kind = Issue.Kind.Snapshot(snapshotting: issue.kind)
255+
self.comments = issue.comments
256+
}
251257
self.sourceContext = issue.sourceContext
252258
self.isKnown = issue.isKnown
253259
}

Tests/TestingTests/ConfirmationTests.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,28 @@ struct UnsuccessfulConfirmationTests {
116116
}
117117

118118
@Test(.hidden, arguments: [
119-
1 ... 2 as any Confirmation.ExpectedCount,
119+
1 ... 2 as any ExpectedCount,
120120
1 ..< 2,
121121
1 ..< 3,
122122
..<2,
123123
...2,
124124
999...,
125125
])
126-
func confirmedOutOfRange(_ range: any Confirmation.ExpectedCount) async {
126+
func confirmedOutOfRange(_ range: any ExpectedCount) async {
127127
await confirmation(expectedCount: range) { (thingHappened) async in
128128
thingHappened(count: 3)
129129
}
130130
}
131131
}
132+
133+
// MARK: -
134+
135+
/// Needed since we don't have generic test functions, so we need a concrete
136+
/// argument type for `confirmedOutOfRange(_:)`, but we can't write
137+
/// `any RangeExpression<Int> & Sendable`. ([96960993](rdar://96960993))
138+
protocol ExpectedCount: RangeExpression, Sendable where Bound == Int {}
139+
extension ClosedRange<Int>: ExpectedCount {}
140+
extension PartialRangeFrom<Int>: ExpectedCount {}
141+
extension PartialRangeThrough<Int>: ExpectedCount {}
142+
extension PartialRangeUpTo<Int>: ExpectedCount {}
143+
extension Range<Int>: ExpectedCount {}

0 commit comments

Comments
 (0)