| // RUN: %target-run-simple-swiftgyb |
| // REQUIRES: executable_test |
| |
| import StdlibUnittest |
| import StdlibCollectionUnittest |
| |
| extension Range { |
| static var _isHalfOpen: Bool { return true } |
| } |
| extension CountableRange { |
| static var _isHalfOpen: Bool { return true } |
| } |
| extension ClosedRange { |
| static var _isHalfOpen: Bool { return false } |
| } |
| extension CountableClosedRange { |
| static var _isHalfOpen: Bool { return false } |
| } |
| |
| protocol TestProtocol1 {} |
| |
| struct ContainsTest { |
| let lowerBound: Int |
| let upperBound: Int |
| let value: Int |
| let loc: SourceLoc |
| |
| var containedInHalfOpen: Bool { |
| return lowerBound <= value && value < upperBound |
| } |
| |
| var containedInClosed: Bool { |
| return lowerBound <= value && value <= upperBound |
| } |
| |
| init( |
| lowerBound: Int, |
| upperBound: Int, |
| value: Int, |
| file: String = #file, line: UInt = #line |
| ) { |
| self.lowerBound = lowerBound |
| self.upperBound = upperBound |
| self.value = value |
| self.loc = SourceLoc(file, line, comment: "test data") |
| } |
| } |
| |
| func generateContainsTests() -> [ContainsTest] { |
| let bounds = [ Int.min, -30, -10, 0, 10, 20, Int.max ] |
| var result: [ContainsTest] = [] |
| for lowerBound in bounds { |
| for upperBound in bounds { |
| if lowerBound > upperBound { continue } |
| for value in bounds { |
| result.append( |
| ContainsTest( |
| lowerBound: lowerBound, upperBound: upperBound, |
| value: value)) |
| } |
| } |
| } |
| return result |
| } |
| |
| let containsTests: [ContainsTest] = generateContainsTests() |
| |
| infix operator ..<* { associativity none precedence 135 } |
| infix operator ...* { associativity none precedence 135 } |
| |
| enum VariantRange { |
| case halfOpen(lowerBound: Int, upperBound: Int) |
| case closed(lowerBound: Int, upperBound: Int) |
| |
| var isHalfOpen: Bool { |
| switch self { |
| case .halfOpen: |
| return true |
| case .closed: |
| return false |
| } |
| } |
| |
| var lowerBound: Int { |
| switch self { |
| case .halfOpen(let result, _): |
| return result |
| case .closed(let result, _): |
| return result |
| } |
| } |
| |
| var upperBound: Int { |
| switch self { |
| case .halfOpen(_, let result): |
| return result |
| case .closed(_, let result): |
| return result |
| } |
| } |
| } |
| |
| func ..<* (lhs: Int, rhs: Int) -> VariantRange { |
| return .halfOpen(lowerBound: lhs, upperBound: rhs) |
| } |
| |
| func ...* (lhs: Int, rhs: Int) -> VariantRange { |
| return .closed(lowerBound: lhs, upperBound: rhs) |
| } |
| |
| struct OverlapsTest { |
| let expected: Bool |
| let lhs: VariantRange |
| let rhs: VariantRange |
| let loc: SourceLoc |
| |
| init( |
| expected: Bool, |
| lhs: VariantRange, |
| rhs: VariantRange, |
| file: String = #file, line: UInt = #line |
| ) { |
| self.expected = expected |
| self.lhs = lhs |
| self.rhs = rhs |
| self.loc = SourceLoc(file, line, comment: "test data") |
| } |
| } |
| |
| let overlapsTests: [OverlapsTest] = [ |
| // 0-4, 5-10 |
| OverlapsTest(expected: false, lhs: 0..<*4, rhs: 5..<*10), |
| OverlapsTest(expected: false, lhs: 0..<*4, rhs: 5...*10), |
| OverlapsTest(expected: false, lhs: 0...*4, rhs: 5..<*10), |
| OverlapsTest(expected: false, lhs: 0...*4, rhs: 5...*10), |
| |
| // 0-5, 5-10 |
| OverlapsTest(expected: false, lhs: 0..<*5, rhs: 5..<*10), |
| OverlapsTest(expected: false, lhs: 0..<*5, rhs: 5...*10), |
| OverlapsTest(expected: true, lhs: 0...*5, rhs: 5..<*10), |
| OverlapsTest(expected: true, lhs: 0...*5, rhs: 5...*10), |
| |
| // 0-6, 5-10 |
| OverlapsTest(expected: true, lhs: 0..<*6, rhs: 5..<*10), |
| OverlapsTest(expected: true, lhs: 0..<*6, rhs: 5...*10), |
| OverlapsTest(expected: true, lhs: 0...*6, rhs: 5..<*10), |
| OverlapsTest(expected: true, lhs: 0...*6, rhs: 5...*10), |
| |
| // 0-20, 5-10 |
| OverlapsTest(expected: true, lhs: 0..<*20, rhs: 5..<*10), |
| OverlapsTest(expected: true, lhs: 0..<*20, rhs: 5...*10), |
| OverlapsTest(expected: true, lhs: 0...*20, rhs: 5..<*10), |
| OverlapsTest(expected: true, lhs: 0...*20, rhs: 5...*10), |
| |
| // 0-0, 0-5 |
| OverlapsTest(expected: false, lhs: 0..<*0, rhs: 0..<*5), |
| OverlapsTest(expected: false, lhs: 0..<*0, rhs: 0...*5), |
| ] |
| |
| struct ClampedTest { |
| let expected: Range<Int> |
| let subject: Range<Int> |
| let limits: Range<Int> |
| let loc: SourceLoc |
| |
| init( |
| expected: Range<Int>, |
| subject: Range<Int>, |
| limits: Range<Int>, |
| file: String = #file, line: UInt = #line |
| ) { |
| self.expected = expected |
| self.subject = subject |
| self.limits = limits |
| self.loc = SourceLoc(file, line, comment: "test data") |
| } |
| } |
| |
| let clampedTests: [ClampedTest] = [ |
| ClampedTest(expected: 5..<5, subject: 0..<3, limits: 5..<10), |
| ClampedTest(expected: 5..<9, subject: 0..<9, limits: 5..<10), |
| ClampedTest(expected: 5..<10, subject: 0..<13, limits: 5..<10), |
| ClampedTest(expected: 7..<9, subject: 7..<9, limits: 5..<10), |
| ClampedTest(expected: 7..<10, subject: 7..<13, limits: 5..<10), |
| ClampedTest(expected: 10..<10, subject: 13..<15, limits: 5..<10), |
| ] |
| |
| struct OffsetByTest { |
| static let start: Int = 10 |
| static let end: Int = 20 |
| let advanceBy: Int |
| |
| /// Instead of advancing from `startIndex`, advance from `endIndex`. |
| let advanceFromEnd: Bool |
| |
| // `nil` if the test should fail for open ranges. |
| let newOpenIndex: Int? |
| // `nil` if the test should fail for closed ranges. |
| let newClosedIndex: Int? |
| |
| let loc: SourceLoc |
| |
| init( |
| advanceBy: Int, newOpenIndex: Int? = nil, newClosedIndex: Int? = nil, |
| advanceFromEnd: Bool = false, file: String = #file, line: UInt = #line |
| ) { |
| self.advanceBy = advanceBy |
| self.newOpenIndex = newOpenIndex |
| self.newClosedIndex = newClosedIndex |
| self.advanceFromEnd = advanceFromEnd |
| self.loc = SourceLoc(file, line, comment: "index(_:offsetBy:) tests for countable ranges") |
| } |
| } |
| |
| let offsetByTests: [OffsetByTest] = [ |
| // Move forward, valid. |
| OffsetByTest(advanceBy: 4, newOpenIndex: 14, newClosedIndex: 14), |
| OffsetByTest(advanceBy: 0, newOpenIndex: 10, newClosedIndex: 10), |
| OffsetByTest(advanceBy: 10, newOpenIndex: 20, newClosedIndex: 20), |
| OffsetByTest(advanceBy: 11, newClosedIndex: 21), |
| |
| // Move forward, invalid. |
| OffsetByTest(advanceBy: 12), |
| OffsetByTest(advanceBy: 25600), |
| |
| // Move backward, valid. |
| OffsetByTest(advanceBy: -9, newOpenIndex: 11, newClosedIndex: 12, |
| advanceFromEnd: true), |
| OffsetByTest(advanceBy: -10, newOpenIndex: 10, newClosedIndex: 11, |
| advanceFromEnd: true), |
| OffsetByTest(advanceBy: -11, newClosedIndex: 10, |
| advanceFromEnd: true), |
| OffsetByTest(advanceBy: 0, newOpenIndex: 20, newClosedIndex: 21, |
| advanceFromEnd: true), |
| |
| // Move backward, invalid. |
| OffsetByTest(advanceBy: -12, advanceFromEnd: true), |
| OffsetByTest(advanceBy: -2885, advanceFromEnd: true), |
| ] |
| |
| struct IndexDistanceTest { |
| static let start: Int = 10 |
| static let end: Int = 30 |
| |
| // Leave either of these `nil` to use the 'right edge index', which differs |
| // between open and closed ranges. |
| let leftIndex: Int? |
| let rightIndex: Int? |
| |
| let loc: SourceLoc |
| let openDistance: Int |
| let closedDistance: Int |
| |
| /// Return the index `leftIndex` steps away from `start`, or the collection's |
| /// `endIndex` if `leftIndex` is `nil`. |
| func leftIndex<C: RandomAccessCollection>(of c: C) -> C.Index { |
| guard let leftIndex = leftIndex else { |
| return c.endIndex |
| } |
| let iterations = leftIndex - IndexDistanceTest.start |
| var idx = c.startIndex |
| for _ in 0..<iterations { |
| idx = c.index(after: idx) |
| } |
| return idx |
| } |
| |
| /// Return the index `rightIndex` steps away from `start`, or the |
| /// collection's `endIndex` if `rightIndex` is `nil`. |
| func rightIndex<C: RandomAccessCollection>(of c: C) -> C.Index { |
| guard let rightIndex = rightIndex else { |
| return c.endIndex |
| } |
| let iterations = rightIndex - IndexDistanceTest.start |
| _precondition(iterations >= 0) |
| var idx = c.startIndex |
| for _ in 0..<iterations { |
| idx = c.index(after: idx) |
| } |
| return idx |
| } |
| |
| init( |
| leftIndex: Int? = nil, rightIndex: Int? = nil, |
| distance: Int, closedDistance: Int? = nil, |
| file: String = #file, line: UInt = #line |
| ) { |
| self.leftIndex = leftIndex |
| self.rightIndex = rightIndex |
| self.openDistance = distance |
| // Only set closedDistance if testing against someRange.endIndex. |
| if closedDistance != nil { |
| _precondition(leftIndex == nil || rightIndex == nil) |
| } |
| self.closedDistance = closedDistance ?? distance |
| self.loc = SourceLoc(file, line, comment: "distance(from:to:) tests for countable ranges") |
| } |
| } |
| |
| let indexDistanceTests: [IndexDistanceTest] = [ |
| // Forward, inside bounds. |
| IndexDistanceTest(leftIndex: 10, rightIndex: 29, distance: 19), |
| IndexDistanceTest(leftIndex: 15, rightIndex: 15, distance: 0), |
| IndexDistanceTest(leftIndex: 16, rightIndex: 29, distance: 13), |
| // Forward, at edge. |
| IndexDistanceTest(leftIndex: 10, distance: 20, closedDistance: 21), |
| IndexDistanceTest(leftIndex: 21, distance: 9, closedDistance: 10), |
| IndexDistanceTest(leftIndex: IndexDistanceTest.end, distance: 0, closedDistance: 1), |
| // Backwards, inside bounds. |
| IndexDistanceTest(leftIndex: 27, rightIndex: 16, distance: -11), |
| IndexDistanceTest(leftIndex: 15, rightIndex: 14, distance: -1), |
| // Backwards, at edge. |
| IndexDistanceTest(rightIndex: 18, distance: -12, closedDistance: -13), |
| IndexDistanceTest(rightIndex: 10, distance: -20, closedDistance: -21), |
| IndexDistanceTest(rightIndex: IndexDistanceTest.end, distance: 0, closedDistance: -1), |
| // Completely at edge. |
| IndexDistanceTest(distance: 0), |
| ] |
| |
| %{ |
| all_range_types = [ |
| ('Range', '..<', 'MinimalComparableValue'), |
| ('CountableRange', '..<', 'MinimalStrideableValue'), |
| ('ClosedRange', '...', 'MinimalComparableValue'), |
| ('CountableClosedRange', '...', 'MinimalStrideableValue'), |
| ] |
| }% |
| |
| % for (Self, op, Bound) in all_range_types: |
| % TestSuite = Self + 'TestSuite' |
| |
| // Check that the generic parameter is called 'Bound'. |
| extension ${Self} where Bound : TestProtocol1 { |
| var _elementIsTestProtocol1: Bool { |
| fatalError("not implemented") |
| } |
| } |
| |
| var ${TestSuite} = TestSuite("${Self}") |
| |
| ${TestSuite}.test("init(uncheckedBounds:)") |
| .forEach(in: [(1, 2), (1, 1), (2, 1)]) { |
| (lowerInt, upperInt) in |
| |
| // Check that 'init(uncheckedBounds:)' does not perform precondition checks, |
| // allowing to create ranges that break invariants. |
| let r = ${Self}( |
| uncheckedBounds: (lower: ${Bound}(lowerInt), upper: ${Bound}(upperInt))) |
| expectEqual(lowerInt, r.lowerBound.value) |
| expectEqual(upperInt, r.upperBound.value) |
| } |
| |
| % for (DestinationSelf, _, _) in all_range_types: |
| ${TestSuite}.test("init(${DestinationSelf})/whereBoundIsStrideable") |
| .forEach(in: [(0, 0), (1, 2), (10, 20), (Int.min, Int.max)]) { |
| (lowerInt, upperInt) in |
| |
| let lower = MinimalStrideableValue(lowerInt) |
| let upper = MinimalStrideableValue(upperInt) |
| let source: ${Self}<MinimalStrideableValue> = lower${op}upper |
| |
| let isSourceHalfOpen = ${Self}<MinimalStrideableValue>._isHalfOpen |
| let isDestinationHalfOpen = |
| ${DestinationSelf}<MinimalStrideableValue>._isHalfOpen |
| |
| let shouldTrap = |
| (source.isEmpty && !isDestinationHalfOpen) || |
| (upperInt == Int.max && !isSourceHalfOpen && isDestinationHalfOpen) |
| if shouldTrap { |
| expectCrashLater() |
| } |
| |
| let converted = ${DestinationSelf}(source) |
| |
| if !shouldTrap { |
| expectEqual(lower.value, converted.lowerBound.value) |
| expectEqual( |
| upper.value + ((isSourceHalfOpen ? 0 : 1) - (isDestinationHalfOpen ? 0 : 1)), |
| converted.upperBound.value) |
| } |
| } |
| % end |
| |
| ${TestSuite}.test("lowerBound, upperBound") { |
| let _1 = ${Bound}(1, identity: 1010) |
| let _2 = ${Bound}(2, identity: 2020) |
| let range: ${Self}<${Bound}> = _1${op}_2 |
| expectEqual(1, range.lowerBound.value) |
| expectEqual(1010, range.lowerBound.identity) |
| expectEqual(2, range.upperBound.value) |
| expectEqual(2020, range.upperBound.identity) |
| } |
| |
| ${TestSuite}.test("Equatable") { |
| let _1 = ${Bound}(1) |
| let _2 = ${Bound}(2) |
| |
| let instances: [${Self}<${Bound}>] = [ |
| _1${op}_1, |
| _1${op}_2, |
| _2${op}_2, |
| ] |
| checkEquatable(instances, oracle: { $0 == $1 }) |
| } |
| |
| ${TestSuite}.test("'${op}' traps when upperBound < lowerBound") |
| .crashOutputMatches(_isDebugAssertConfiguration() ? |
| "Can't form Range with upperBound < lowerBound" : "") |
| .code { |
| let _1 = ${Bound}(1) |
| let _2 = ${Bound}(2) |
| expectCrashLater() |
| let range: ${Self}<${Bound}> = _2${op}_1 |
| _blackHole(range) |
| } |
| |
| ${TestSuite}.test("contains(_:)/staticDispatch") { |
| let start = ${Bound}(10) |
| let end = ${Bound}(20) |
| let range: ${Self}<${Bound}> = start${op}end |
| expectEqual(1, ${Bound}.timesLessWasCalled.value) |
| for test in 0..<30 { |
| % if 'Closed' in Self: |
| let expected = test >= start.value && test <= end.value |
| % else: |
| let expected = test >= start.value && test < end.value |
| % end |
| expectEqual( |
| expected, range.contains(${Bound}(test)), |
| "test=\(test)") |
| } |
| expectEqual(51, ${Bound}.timesLessWasCalled.value) |
| } |
| |
| ${TestSuite}.test("~=/staticDispatch") { |
| let start = ${Bound}(10) |
| let end = ${Bound}(20) |
| let range: ${Self}<${Bound}> = start${op}end |
| expectEqual(1, ${Bound}.timesLessWasCalled.value) |
| for test in 0..<30 { |
| % if 'Closed' in Self: |
| let expected = test >= start.value && test <= end.value |
| % else: |
| let expected = test >= start.value && test < end.value |
| % end |
| expectEqual( |
| expected, range ~= ${Bound}(test), |
| "test=\(test)") |
| } |
| expectEqual(51, ${Bound}.timesLessWasCalled.value) |
| } |
| |
| % if 'Countable' in Self: |
| ${TestSuite}.test("contains(_:)/dynamicDispatch") { |
| let start = ${Bound}(10) |
| let end = ${Bound}(20) |
| let range: ${Self}<${Bound}> = start${op}end |
| let loggingRange = LoggingCollection(wrapping: range) |
| expectEqual(1, ${Bound}.timesLessWasCalled.value) |
| for test in 0..<30 { |
| % if 'Closed' in Self: |
| let expected = test >= start.value && test <= end.value |
| % else: |
| let expected = test >= start.value && test < end.value |
| % end |
| expectEqual( |
| expected, loggingRange.contains(${Bound}(test)), |
| "test=\(test)") |
| } |
| expectEqual(51, MinimalStrideableValue.timesLessWasCalled.value) |
| } |
| % end |
| |
| ${TestSuite}.test("contains(_:)/semantics, ~=/semantics") |
| .forEach(in: containsTests) { |
| (test) in |
| |
| // Check both static and dynamic dispatch. |
| let range: ${Self}<${Bound}> = ${Bound}(test.lowerBound)${op}${Bound}(test.upperBound) |
| % if 'Countable' in Self: |
| let loggingRange = LoggingCollection(wrapping: range) |
| % else: |
| let loggingRange = range |
| % end |
| |
| let value = ${Bound}(test.value) |
| let expected = |
| ${Self}<${Bound}>._isHalfOpen |
| ? test.containedInHalfOpen |
| : test.containedInClosed |
| expectEqual(expected, range.contains(value)) |
| expectEqual(expected, loggingRange.contains(value)) |
| expectEqual(expected, range ~= value) |
| } |
| |
| % for (OtherSelf, other_op, OtherBound) in all_range_types: |
| ${TestSuite}.test("overlaps(${OtherSelf})/semantics") |
| .forEach(in: overlapsTests) { |
| (test) in |
| |
| if test.lhs.isHalfOpen != ${Self}<${Bound}>._isHalfOpen || |
| test.rhs.isHalfOpen != ${OtherSelf}<${OtherBound}>._isHalfOpen { |
| return |
| } |
| |
| let lhs: ${Self}<${Bound}> |
| = ${Bound}(test.lhs.lowerBound)${op}${Bound}(test.lhs.upperBound) |
| let rhs: ${Self}<${Bound}> |
| = ${Bound}(test.rhs.lowerBound)${op}${Bound}(test.rhs.upperBound) |
| |
| expectEqual(test.expected, lhs.overlaps(rhs)) |
| expectEqual(test.expected, rhs.overlaps(lhs)) |
| |
| expectEqual(!lhs.isEmpty, lhs.overlaps(lhs)) |
| expectEqual(!rhs.isEmpty, rhs.overlaps(rhs)) |
| } |
| % end |
| |
| ${TestSuite}.test("clamped(to:)/semantics") |
| .forEach(in: clampedTests) { |
| (test) in |
| |
| let subject: ${Self}<${Bound}> |
| = ${Bound}(test.subject.lowerBound)${op}${Bound}(test.subject.upperBound) |
| let limits: ${Self}<${Bound}> |
| = ${Bound}(test.limits.lowerBound)${op}${Bound}(test.limits.upperBound) |
| |
| expectEqual( |
| ${Bound}(test.expected.lowerBound)${op}${Bound}(test.expected.upperBound), |
| subject.clamped(to: limits)) |
| } |
| |
| ${TestSuite}.test("count/whereBoundIsStrideable") { |
| typealias Bound = MinimalStrideableValue |
| let start = Bound(10) |
| let end = Bound(20) |
| let range1: ${Self}<Bound> = start${op}start |
| let range2: ${Self}<Bound> = start${op}end |
| expectEqual(0, Bound.timesEqualEqualWasCalled.value) |
| expectEqual(2, Bound.timesLessWasCalled.value) |
| expectEqual(0, Bound.timesDistanceWasCalled.value) |
| expectEqual(0, Bound.timesAdvancedWasCalled.value) |
| expectEqual( 0 + (${Self}<Bound>._isHalfOpen ? 0 : 1), range1.count) |
| expectEqual(10 + (${Self}<Bound>._isHalfOpen ? 0 : 1), range2.count) |
| expectEqual(0, Bound.timesEqualEqualWasCalled.value) |
| expectEqual(2, Bound.timesLessWasCalled.value) |
| expectEqual(2, Bound.timesDistanceWasCalled.value) |
| expectEqual(0, Bound.timesAdvancedWasCalled.value) |
| } |
| |
| % if 'Countable' in Self: |
| ${TestSuite}.test("index(after:)/semantics").forEach(in: [3, 5, 10, 12, 86]) { |
| (endValue) in |
| let start = ${Bound}(0) |
| let end = ${Bound}(endValue) |
| let range: ${Self}<${Bound}> = start${op}end |
| var idx = range.startIndex |
| var previousValue: Int? |
| |
| for _ in 0${op}(endValue - 1) { |
| // Advance the index to the last in-range position. |
| idx = range.index(after: idx) |
| if let previousValue = previousValue { |
| expectEqual(range[idx].value, previousValue + 1) |
| } |
| previousValue = range[idx].value |
| } |
| // Iterate once more to get to the 'after end' position. |
| idx = range.index(after: idx) |
| expectEqual(idx, range.endIndex) |
| // Now, iterate once more and expect a crash. |
| expectCrashLater() |
| _ = range.index(after: idx) |
| } |
| |
| ${TestSuite}.test("index(after:)/semantics/smallestRange") { |
| let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0) |
| var idx = range.startIndex |
| |
| % if 'Closed' in Self: |
| // Should be able to advance the index once. |
| idx = range.index(after: idx) |
| % end |
| |
| expectCrashLater() |
| _ = range.index(after: idx) |
| } |
| |
| ${TestSuite}.test("index(before:)/semantics").forEach(in: [3, 5, 10, 12, 86]) { |
| (endValue) in |
| let start = ${Bound}(0) |
| let end = ${Bound}(endValue) |
| let range: ${Self}<${Bound}> = start${op}end |
| var idx = range.endIndex |
| var previousValue: Int? |
| |
| for _ in 0${op}(endValue) { |
| // Advance the index backwards until we reach `startIndex`. |
| // Precondition: for a..<b, advance endIndex (b - a) times to reach startIndex. |
| // Precondition: for a...b, advance endIndex (b - a) + 1 times to reach startIndex. |
| idx = range.index(before: idx) |
| if let previousValue = previousValue { |
| expectEqual(range[idx].value, previousValue - 1) |
| } |
| previousValue = range[idx].value |
| } |
| // At this point, we should be at `startIndex`. |
| expectEqual(idx, range.startIndex) |
| // Now, iterate once more and expect a crash. |
| expectCrashLater() |
| _ = range.index(before: idx) |
| } |
| |
| ${TestSuite}.test("index(before:)/semantics/smallestRange") { |
| let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0) |
| var idx = range.endIndex |
| |
| % if 'Closed' in Self: |
| // Should be able to advance the index once. |
| idx = range.index(before: idx) |
| % end |
| |
| expectCrashLater() |
| _ = range.index(before: idx) |
| } |
| |
| ${TestSuite}.test("index(offsetBy:)/semantics").forEach(in: offsetByTests) { |
| (test) in |
| let start = ${Bound}(OffsetByTest.start) |
| let end = ${Bound}(OffsetByTest.end) |
| let range: ${Self}<${Bound}> = start${op}end |
| let initialIdx = test.advanceFromEnd ? range.endIndex : range.startIndex |
| |
| % if 'Closed' in Self: |
| if let newIndex = test.newClosedIndex { |
| % else: |
| if let newIndex = test.newOpenIndex { |
| % end |
| let idx = range.index(initialIdx, offsetBy: test.advanceBy) |
| if idx != range.endIndex { |
| expectEqual(newIndex, range[idx].value, stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } else { |
| expectCrashLater() |
| _ = range.index(initialIdx, offsetBy: test.advanceBy) |
| } |
| } |
| |
| ${TestSuite}.test("index(offsetBy:)/semantics/smallestRange/forward") { |
| let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0) |
| var idx = range.startIndex |
| |
| // This should work. |
| _ = range.index(idx, offsetBy: 0) |
| |
| % if 'Closed' in Self: |
| // Can advance once more. |
| _ = range.index(idx, offsetBy: 1) |
| expectCrashLater() |
| _ = range.index(idx, offsetBy: 2) |
| % else: |
| expectCrashLater() |
| _ = range.index(idx, offsetBy: 1) |
| % end |
| } |
| |
| ${TestSuite}.test("index(offsetBy:)/semantics/smallestRange/backward") { |
| let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0) |
| var idx = range.endIndex |
| |
| // This should work. |
| _ = range.index(idx, offsetBy: 0) |
| |
| % if 'Closed' in Self: |
| // Can advance once more. |
| _ = range.index(idx, offsetBy: -1) |
| expectCrashLater() |
| _ = range.index(idx, offsetBy: -2) |
| % else: |
| expectCrashLater() |
| _ = range.index(idx, offsetBy: -1) |
| % end |
| } |
| |
| ${TestSuite}.test("distance(from:to:)/semantics") { |
| for test in indexDistanceTests { |
| let start = ${Bound}(IndexDistanceTest.start) |
| let end = ${Bound}(IndexDistanceTest.end) |
| let range: ${Self}<${Bound}> = start${op}end |
| let leftIdx = test.leftIndex(of: range) |
| let rightIdx = test.rightIndex(of: range) |
| |
| let distance = range.distance(from: leftIdx, to: rightIdx) |
| % if 'Closed' in Self: |
| expectEqual(test.closedDistance, distance, stackTrace: SourceLocStack().with(test.loc)) |
| % else: |
| expectEqual(test.openDistance, distance, stackTrace: SourceLocStack().with(test.loc)) |
| % end |
| } |
| } |
| |
| ${TestSuite}.test("distance(from:to:)/semantics/smallestRange") { |
| let range: ${Self}<${Bound}> = ${Bound}(0)${op}${Bound}(0) |
| var idx = range.endIndex |
| |
| % if 'Closed' in Self: |
| expectEqual(1, range.distance(from: range.startIndex, to: range.endIndex)) |
| % else: |
| expectEqual(0, range.distance(from: range.startIndex, to: range.endIndex)) |
| % end |
| } |
| |
| % end |
| |
| ${TestSuite}.test("isEmpty") { |
| let start = ${Bound}(10) |
| let end = ${Bound}(20) |
| let range1: ${Self}<${Bound}> = start${op}start |
| let range2: ${Self}<${Bound}> = start${op}end |
| expectEqual(0, ${Bound}.timesEqualEqualWasCalled.value) |
| expectEqual(2, ${Bound}.timesLessWasCalled.value) |
| expectEqual(${Self}<${Bound}>._isHalfOpen, range1.isEmpty) |
| expectFalse(range2.isEmpty) |
| expectEqual( |
| ${Self}<${Bound}>._isHalfOpen ? 2 : 0, |
| ${Bound}.timesEqualEqualWasCalled.value) |
| expectEqual(2, ${Bound}.timesLessWasCalled.value) |
| % if Bound == 'MinimalStrideableValue': |
| expectEqual(0, ${Bound}.timesDistanceWasCalled.value) |
| expectEqual(0, ${Bound}.timesAdvancedWasCalled.value) |
| % end |
| } |
| |
| ${TestSuite}.test("CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable") { |
| var r: ${Self}<CustomPrintableValue> = |
| CustomPrintableValue(1)${op}CustomPrintableValue(2) |
| expectPrinted("(value: 1).description${op}(value: 2).description", r) |
| expectDebugPrinted( |
| "${Self}(" + |
| "(value: 1).debugDescription${op}(value: 2).debugDescription" + |
| ")", |
| r) |
| expectDumped( |
| "▿ ${Self}((value: 1).debugDescription${op}(value: 2).debugDescription)\n" + |
| " ▿ lowerBound: (value: 1).debugDescription\n" + |
| " - value: 1\n" + |
| " - identity: 0\n" + |
| " ▿ upperBound: (value: 2).debugDescription\n" + |
| " - value: 2\n" + |
| " - identity: 0\n", |
| r) |
| } |
| |
| % end |
| |
| CountableRangeTestSuite.test("AssociatedTypes") { |
| typealias Collection = CountableRange<MinimalStrideableValue> |
| expectCollectionAssociatedTypes( |
| collectionType: Collection.self, |
| iteratorType: IndexingIterator<Collection>.self, |
| subSequenceType: Collection.self, |
| indexType: MinimalStrideableValue.self, |
| indicesType: Collection.self) |
| } |
| |
| CountableClosedRangeTestSuite.test("AssociatedTypes") { |
| typealias Collection = CountableClosedRange<MinimalStrideableValue> |
| expectCollectionAssociatedTypes( |
| collectionType: Collection.self, |
| iteratorType: IndexingIterator<Collection>.self, |
| subSequenceType: RandomAccessSlice<Collection>.self, |
| indexType: ClosedRangeIndex<MinimalStrideableValue>.self, |
| indicesType: DefaultRandomAccessIndices<Collection>.self) |
| } |
| |
| var MiscTestSuite = TestSuite("Misc") |
| |
| MiscTestSuite.test("map()") { |
| // <rdar://problem/17054014> map method should exist on ranges |
| var result = (1..<4).map { $0*2 } |
| expectType(Array<Int>.self, &result) |
| expectEqualSequence([ 2, 4, 6 ], result) |
| } |
| |
| MiscTestSuite.test("reversed()") { |
| var result = (0..<10).lazy.reversed() |
| typealias Expected = LazyCollection< |
| ReversedRandomAccessCollection<CountableRange<Int>>> |
| expectType(Expected.self, &result) |
| expectEqualSequence( |
| [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ], |
| result) |
| } |
| |
| runAllTests() |
| |