diff --git a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift index 1a269a941..91c0bf4fe 100644 --- a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift +++ b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift @@ -165,7 +165,7 @@ private func _callBinaryOperator( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkBinaryOperation( +@_disfavoredOverload public func __checkBinaryOperation( _ lhs: T, _ op: (T, () -> U) -> Bool, _ rhs: @autoclosure () -> U, expression: __Expression, comments: @autoclosure () -> [Comment], @@ -594,7 +594,7 @@ public func __checkPropertyAccess( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkBinaryOperation( +@_disfavoredOverload public func __checkBinaryOperation( _ lhs: T, _ op: (T, () -> T) -> Bool, _ rhs: @autoclosure () -> T, expression: __Expression, comments: @autoclosure () -> [Comment], @@ -660,6 +660,34 @@ public func __checkBinaryOperation( ) } +/// Check that an expectation has passed after a condition has been evaluated +/// and throw an error if it failed. +/// +/// This overload is necessary because ranges are collections and satisfy the +/// requirements for the difference-calculating overload above, but it doesn't +/// make sense to diff them and very large ranges can cause overflows or hangs. +/// +/// - Warning: This function is used to implement the `#expect()` and +/// `#require()` macros. Do not call it directly. +public func __checkBinaryOperation( + _ lhs: T, _ op: (T, () -> U) -> Bool, _ rhs: @autoclosure () -> U, + expression: __Expression, + comments: @autoclosure () -> [Comment], + isRequired: Bool, + sourceLocation: SourceLocation +) -> Result where T: RangeExpression, U: RangeExpression { + let (condition, rhs) = _callBinaryOperator(lhs, op, rhs) + return __checkValue( + condition, + expression: expression, + expressionWithCapturedRuntimeValues: expression.capturingRuntimeValues(lhs, rhs), + difference: nil, + comments: comments(), + isRequired: isRequired, + sourceLocation: sourceLocation + ) +} + /// Check that an expectation has passed after a condition has been evaluated /// and throw an error if it failed. /// diff --git a/Tests/TestingTests/IssueTests.swift b/Tests/TestingTests/IssueTests.swift index e0618e344..e31041464 100644 --- a/Tests/TestingTests/IssueTests.swift +++ b/Tests/TestingTests/IssueTests.swift @@ -1104,6 +1104,38 @@ final class IssueTests: XCTestCase { }.run(configuration: configuration) } + func testCollectionDifferenceSkippedForRanges() async { + var configuration = Configuration() + configuration.eventHandler = { event, _ in + guard case let .issueRecorded(issue) = event.kind else { + return + } + guard case let .expectationFailed(expectation) = issue.kind else { + XCTFail("Unexpected issue kind \(issue.kind)") + return + } + XCTAssertNil(expectation.differenceDescription) + } + + await Test { + let range_int8: ClosedRange = Int(Int8.min)...Int(Int8.max) + let range_uint16: ClosedRange = Int(UInt16.min)...Int(UInt16.max) + let range_int64: ClosedRange = Int(Int64.min)...Int(Int64.max) + + #expect(range_int8 == (-127)...127, "incorrect min") + #expect(range_int8 == (-128)...128, "incorrect max") + #expect(range_int8 == 0...0, "both incorrect") + + #expect(range_uint16 == (-1)...65_535, "incorrect min") + #expect(range_uint16 == 0...65_534, "incorrect max") + #expect(range_uint16 == 1...1, "both incorrect") + + #expect(range_int64 == (-9_223_372_036_854_775_807)...9_223_372_036_854_775_807, "incorrect min") + #expect(range_int64 == (-9_223_372_036_854_775_808)...9_223_372_036_854_775_806, "incorrect max") + #expect(range_int64 == 0...0, "both incorrect") + }.run(configuration: configuration) + } + func testNegatedExpressions() async { var configuration = Configuration() configuration.eventHandler = { event, _ in