// RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test // REQUIRES: reflection import StdlibUnittest import StdlibCollectionUnittest extension Range { static var _isHalfOpen: Bool { return true } } extension ClosedRange { 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 ..<* : RangeFormationPrecedence infix operator ...* : RangeFormationPrecedence 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") } } typealias ContainsRangeTest = OverlapsTest let containsRangeTests: [ContainsRangeTest] = [ // Same bounds ContainsRangeTest(expected: true, lhs: 0..<*10, rhs: 0..<*10), ContainsRangeTest(expected: false, lhs: 0..<*10, rhs: 0...*10), ContainsRangeTest(expected: true, lhs: 0..<*10, rhs: 0...*9), ContainsRangeTest(expected: true, lhs: 0...*10, rhs: 0...*10), ContainsRangeTest(expected: true, lhs: 0...*10, rhs: 0..<*10), ContainsRangeTest(expected: true, lhs: 0...*10, rhs: 0..<*11), // Interior ContainsRangeTest(expected: true, lhs: 0..<*10, rhs: 1..<*9), ContainsRangeTest(expected: true, lhs: 0..<*10, rhs: 1...*9), ContainsRangeTest(expected: true, lhs: 0...*10, rhs: 1...*9), ContainsRangeTest(expected: true, lhs: 0...*10, rhs: 1..<*9), // Failures ContainsRangeTest(expected: false, lhs: 0..<*10, rhs: -10..<*5), ContainsRangeTest(expected: false, lhs: 0..<*10, rhs: -10...*5), ContainsRangeTest(expected: false, lhs: 0...*10, rhs: -10...*5), ContainsRangeTest(expected: false, lhs: 0...*10, rhs: -10..<*5), ContainsRangeTest(expected: false, lhs: 0..<*10, rhs: 5..<*15), ContainsRangeTest(expected: false, lhs: 0..<*10, rhs: 5...*15), ContainsRangeTest(expected: false, lhs: 0...*10, rhs: 5...*15), ContainsRangeTest(expected: false, lhs: 0...*10, rhs: 5..<*15), // "Empty" ranges ContainsRangeTest(expected: true, lhs: 0..<*0, rhs: 0..<*0), ContainsRangeTest(expected: true, lhs: 0..<*0, rhs: 1..<*1), ContainsRangeTest(expected: false, lhs: 0..<*0, rhs: 0...*0), ContainsRangeTest(expected: true, lhs: 0...*0, rhs: 0...*0), ContainsRangeTest(expected: true, lhs: 0...*0, rhs: 0..<*0), ContainsRangeTest(expected: true, lhs: 0...*0, rhs: 1..<*1), ] 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 let subject: Range let limits: Range let loc: SourceLoc init( expected: Range, subject: Range, limits: Range, 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(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..(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.. = lower${op}upper let isSourceHalfOpen = ${Self}._isHalfOpen let isDestinationHalfOpen = ${DestinationSelf}._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() ? "Range requires lowerBound <= upperBound" : "") .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 'Strideable' in Bound: ${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 'Strideable' in Bound: 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, _, _) 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}<${Bound}>._isHalfOpen { return } let lhs: ${Self}<${Bound}> = ${Bound}(test.lhs.lowerBound)${op}${Bound}(test.lhs.upperBound) let rhs: ${OtherSelf}<${Bound}> = ${Bound}(test.rhs.lowerBound)${other_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)) } // ClosedRange.contains(_: Range) is only available for stridable bounds % if Self == 'Range' or (Self == 'ClosedRange' and OtherSelf == 'ClosedRange') or 'Stridable' in Bound: ${TestSuite}.test("contains(${OtherSelf})/semantics") .forEach(in: containsRangeTests) { (test) in if test.lhs.isHalfOpen != ${Self}<${Bound}>._isHalfOpen || test.rhs.isHalfOpen != ${OtherSelf}<${Bound}>._isHalfOpen { return } let lhs: ${Self}<${Bound}> = ${Bound}(test.lhs.lowerBound)${op}${Bound}(test.lhs.upperBound) let rhs: ${OtherSelf}<${Bound}> = ${Bound}(test.rhs.lowerBound)${other_op}${Bound}(test.rhs.upperBound) expectEqual(test.expected, lhs.contains(rhs)) } % end % 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} = start${op}start let range2: ${Self} = 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}._isHalfOpen ? 0 : 1), range1.count) expectEqual(10 + (${Self}._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 'Strideable' in Bound: ${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.. = ${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(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 = Range expectCollectionAssociatedTypes( collectionType: Collection.self, iteratorType: Collection.Iterator.self, subSequenceType: Collection.self, indexType: MinimalStrideableValue.self, indicesType: Collection.self) } CountableClosedRangeTestSuite.test("AssociatedTypes") { typealias Collection = ClosedRange expectCollectionAssociatedTypes( collectionType: Collection.self, iteratorType: Collection.Iterator.self, subSequenceType: Slice.self, indexType: Collection.Index.self, indicesType: DefaultIndices.self) } var MiscTestSuite = TestSuite("Misc") MiscTestSuite.test("map()") { // map method should exist on ranges var result = (1..<4).map { $0*2 } expectType(Array.self, &result) expectEqualSequence([ 2, 4, 6 ], result) } MiscTestSuite.test("reversed()") { var result = (0..<10).lazy.reversed() typealias Expected = ReversedCollection< LazyCollection>> expectType(Expected.self, &result) expectEqualSequence( [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ], result) } MiscTestSuite.test("Compatibility typealiases") { let _: CountablePartialRangeFrom = 1... let _: CountableRange = MinimalStrideableValue(3)..