// 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 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), ] %{ 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} = 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() ? "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} = 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) % if Self == 'CountableClosedRange': // FIXME: swift-3-indexing-model: implement an O(1) `distance()` on // CountableClosedRange. expectEqual(12, Bound.timesEqualEqualWasCalled.value) expectEqual(2, Bound.timesLessWasCalled.value) expectEqual(0, Bound.timesDistanceWasCalled.value) expectEqual(10, Bound.timesAdvancedWasCalled.value) % else: expectEqual(0, Bound.timesEqualEqualWasCalled.value) expectEqual(2, Bound.timesLessWasCalled.value) expectEqual(2, Bound.timesDistanceWasCalled.value) expectEqual(0, Bound.timesAdvancedWasCalled.value) % 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 = CountableRange expectCollectionAssociatedTypes( collectionType: Collection.self, iteratorType: IndexingIterator.self, subSequenceType: Collection.self, indexType: MinimalStrideableValue.self, indexDistanceType: MinimalStrideableValue.Stride.self, indicesType: Collection.self) } CountableClosedRangeTestSuite.test("AssociatedTypes") { typealias Collection = CountableClosedRange expectCollectionAssociatedTypes( collectionType: Collection.self, iteratorType: ClosedRangeIterator.self, subSequenceType: RandomAccessSlice.self, indexType: ClosedRangeIndex.self, indexDistanceType: MinimalStrideableValue.Stride.self, indicesType: DefaultRandomAccessIndices.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 = LazyRandomAccessCollection< ReversedRandomAccessCollection>> expectType(Expected.self, &result) expectEqualSequence( [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ], result) } runAllTests()