Skip to content

Commit 2e2b210

Browse files
committed
Special-case comparisons of ranges in __checkBinaryOperation().
This PR adds an overload of `__checkBinaryOperation()` that is used when the inputs are range expressions. Ranges do not play well with collection diffing so we want to disable that functionality for them. Resolves #639. Resolves rdar://131122002.
1 parent 03c6518 commit 2e2b210

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

Sources/Testing/Expectations/ExpectationChecking+Macro.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ private func _callBinaryOperator<T, U, R>(
165165
///
166166
/// - Warning: This function is used to implement the `#expect()` and
167167
/// `#require()` macros. Do not call it directly.
168-
public func __checkBinaryOperation<T, U>(
168+
@_disfavoredOverload public func __checkBinaryOperation<T, U>(
169169
_ lhs: T, _ op: (T, () -> U) -> Bool, _ rhs: @autoclosure () -> U,
170170
expression: __Expression,
171171
comments: @autoclosure () -> [Comment],
@@ -594,7 +594,7 @@ public func __checkPropertyAccess<T, U>(
594594
///
595595
/// - Warning: This function is used to implement the `#expect()` and
596596
/// `#require()` macros. Do not call it directly.
597-
public func __checkBinaryOperation<T>(
597+
@_disfavoredOverload public func __checkBinaryOperation<T>(
598598
_ lhs: T, _ op: (T, () -> T) -> Bool, _ rhs: @autoclosure () -> T,
599599
expression: __Expression,
600600
comments: @autoclosure () -> [Comment],
@@ -660,6 +660,34 @@ public func __checkBinaryOperation(
660660
)
661661
}
662662

663+
/// Check that an expectation has passed after a condition has been evaluated
664+
/// and throw an error if it failed.
665+
///
666+
/// This overload is necessary because ranges are collections and satisfy the
667+
/// requirements for the difference-calculating overload above, but it doesn't
668+
/// make sense to diff them and very large ranges can cause overflows or hangs.
669+
///
670+
/// - Warning: This function is used to implement the `#expect()` and
671+
/// `#require()` macros. Do not call it directly.
672+
public func __checkBinaryOperation<T, U>(
673+
_ lhs: T, _ op: (T, () -> U) -> Bool, _ rhs: @autoclosure () -> U,
674+
expression: __Expression,
675+
comments: @autoclosure () -> [Comment],
676+
isRequired: Bool,
677+
sourceLocation: SourceLocation
678+
) -> Result<Void, any Error> where T: RangeExpression, U: RangeExpression {
679+
let (condition, rhs) = _callBinaryOperator(lhs, op, rhs)
680+
return __checkValue(
681+
condition,
682+
expression: expression,
683+
expressionWithCapturedRuntimeValues: expression.capturingRuntimeValues(lhs, rhs),
684+
difference: nil,
685+
comments: comments(),
686+
isRequired: isRequired,
687+
sourceLocation: sourceLocation
688+
)
689+
}
690+
663691
/// Check that an expectation has passed after a condition has been evaluated
664692
/// and throw an error if it failed.
665693
///

Tests/TestingTests/IssueTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,38 @@ final class IssueTests: XCTestCase {
11041104
}.run(configuration: configuration)
11051105
}
11061106

1107+
func testCollectionDifferenceSkippedForRanges() async {
1108+
var configuration = Configuration()
1109+
configuration.eventHandler = { event, _ in
1110+
guard case let .issueRecorded(issue) = event.kind else {
1111+
return
1112+
}
1113+
guard case let .expectationFailed(expectation) = issue.kind else {
1114+
XCTFail("Unexpected issue kind \(issue.kind)")
1115+
return
1116+
}
1117+
XCTAssertNil(expectation.differenceDescription)
1118+
}
1119+
1120+
await Test {
1121+
let range_int8: ClosedRange<Int> = Int(Int8.min)...Int(Int8.max)
1122+
let range_uint16: ClosedRange<Int> = Int(UInt16.min)...Int(UInt16.max)
1123+
let range_int64: ClosedRange<Int> = Int(Int64.min)...Int(Int64.max)
1124+
1125+
#expect(range_int8 == (-127)...127, "incorrect min")
1126+
#expect(range_int8 == (-128)...128, "incorrect max")
1127+
#expect(range_int8 == 0...0, "both incorrect")
1128+
1129+
#expect(range_uint16 == (-1)...65_535, "incorrect min")
1130+
#expect(range_uint16 == 0...65_534, "incorrect max")
1131+
#expect(range_uint16 == 1...1, "both incorrect")
1132+
1133+
#expect(range_int64 == (-9_223_372_036_854_775_807)...9_223_372_036_854_775_807, "incorrect min")
1134+
#expect(range_int64 == (-9_223_372_036_854_775_808)...9_223_372_036_854_775_806, "incorrect max")
1135+
#expect(range_int64 == 0...0, "both incorrect")
1136+
}.run(configuration: configuration)
1137+
}
1138+
11071139
func testNegatedExpressions() async {
11081140
var configuration = Configuration()
11091141
configuration.eventHandler = { event, _ in

0 commit comments

Comments
 (0)