Skip to content

Commit 8cb653d

Browse files
authored
Fix a crash when describing a confirmation failure with a range. (#806)
I was using `relative(to:)` to convert any arbitrary `RangeExpression` to one with an upper and lower bound, but that function may produce invalid ranges for expressions like `1...` that cause crashes. This PR switches to a different, somewhat sillier implementation that should not run into this issue. Resolves #805. Resolves rdar://139222774. ### 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 93eb4ed commit 8cb653d

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

Sources/Testing/Issues/Issue.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,9 @@ extension Issue: CustomStringConvertible, CustomDebugStringConvertible {
175175
/// In the future, when our minimum deployment target supports casting a value
176176
/// to a constrained existential type ([SE-0353](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0353-constrained-existential-types.md#effect-on-abi-stability)),
177177
/// we can remove this protocol and cast to `RangeExpression<Int>` instead.
178-
private protocol _RangeExpressionOverIntValues: RangeExpression where Bound == Int {}
178+
private protocol _RangeExpressionOverIntValues: RangeExpression & Sequence where Bound == Int, Element == Int {}
179179
extension ClosedRange<Int>: _RangeExpressionOverIntValues {}
180180
extension PartialRangeFrom<Int>: _RangeExpressionOverIntValues {}
181-
extension PartialRangeThrough<Int>: _RangeExpressionOverIntValues {}
182-
extension PartialRangeUpTo<Int>: _RangeExpressionOverIntValues {}
183181
extension Range<Int>: _RangeExpressionOverIntValues {}
184182

185183
extension Issue.Kind: CustomStringConvertible {
@@ -200,9 +198,15 @@ extension Issue.Kind: CustomStringConvertible {
200198
}
201199
case let .confirmationMiscounted(actual: actual, expected: expected):
202200
if let expected = expected as? any _RangeExpressionOverIntValues {
203-
let expected = expected.relative(to: [])
204-
if expected.upperBound > expected.lowerBound && expected.lowerBound == expected.upperBound - 1 {
205-
return "Confirmation was confirmed \(actual.counting("time")), but expected to be confirmed \(expected.lowerBound.counting("time"))"
201+
let lowerBound = expected.first { _ in true }
202+
if let lowerBound {
203+
// Not actually an upper bound, just "any value greater than the lower
204+
// bound." That's sufficient for us to determine if the range contains
205+
// a single value.
206+
let upperBound = expected.first { $0 > lowerBound }
207+
if let upperBound, upperBound > lowerBound && lowerBound == upperBound - 1 {
208+
return "Confirmation was confirmed \(actual.counting("time")), but expected to be confirmed \(lowerBound.counting("time"))"
209+
}
206210
}
207211
}
208212
return "Confirmation was confirmed \(actual.counting("time")), but expected to be confirmed \(String(describingForTest: expected)) time(s)"

Tests/TestingTests/ConfirmationTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ struct ConfirmationTests {
4747
}
4848
}
4949

50+
@Test func confirmationFailureCanBeDescribed() async {
51+
var configuration = Configuration()
52+
configuration.eventHandler = { event, _ in
53+
if case let .issueRecorded(issue) = event.kind {
54+
#expect(String(describing: issue) != "")
55+
}
56+
}
57+
58+
await Test {
59+
await confirmation(expectedCount: 1...) { _ in }
60+
}.run(configuration: configuration)
61+
}
62+
5063
#if !SWT_NO_EXIT_TESTS
5164
@Test("Confirmation requires positive count")
5265
func positiveCount() async {

0 commit comments

Comments
 (0)