//===--- Lazy.swift - Tests for LazySequence and LazyCollection -----------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test import StdlibUnittest import StdlibCollectionUnittest % from gyb_stdlib_support import TRAVERSALS, collectionForTraversal var LazyTestSuite = TestSuite("Lazy") protocol TestProtocol1 {} //===----------------------------------------------------------------------===// // Repeated //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension Repeated where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("Repeated") { checkRandomAccessCollection( [] as Array>, repeatElement(OpaqueValue(42), count: 0)) { $0.value == $1.value } checkRandomAccessCollection( [ OpaqueValue(42) ] as Array>, repeatElement(OpaqueValue(42), count: 1)) { $0.value == $1.value } checkRandomAccessCollection( [ OpaqueValue(42), OpaqueValue(42), OpaqueValue(42) ] as Array>, repeatElement(OpaqueValue(42), count: 3)) { $0.value == $1.value } } // FIXME: trap tests. //===----------------------------------------------------------------------===// // IteratorOverOne //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension IteratorOverOne where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("IteratorOverOne") { checkIterator( [] as Array>, IteratorOverOne(_elements: nil as Optional>)) { $0.value == $1.value } checkIterator( [ OpaqueValue(42) ] as Array>, IteratorOverOne(_elements: OpaqueValue(42))) { $0.value == $1.value } } //===----------------------------------------------------------------------===// // CollectionOfOne //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension CollectionOfOne where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("CollectionOfOne") { let c = CollectionOfOne(OpaqueValue(42)) expectEqual(0, c.startIndex) expectEqual(1, c.endIndex) expectEqual(0..<1, c.indices) checkRandomAccessCollection( [ OpaqueValue(42) ], c) { $0.value == $1.value } } LazyTestSuite.test("CollectionOfOne/AssociatedTypes") { typealias Subject = CollectionOfOne> expectRandomAccessCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: IteratorOverOne>.self, subSequenceType: MutableRandomAccessSlice.self, indexType: Int.self, indexDistanceType: Int.self, indicesType: CountableRange.self) } % for (name, operation, indices) in [ % ('index(after:)', '_ = c.index(after: i)', '-2, -1, 1, 2'), % ('index(before:)', '_ = c.index(before: i)', '-1, 0, 2'), % ('subscript(Index)/Get', '_ = c[i]', '-2, -1, 1, 2'), % ('subscript(Index)/Set', 'c[i] = OpaqueValue(42)', '-2, -1, 1, 2'), % ]: LazyTestSuite.test("CollectionOfOne/${name}") .forEach(in: [${indices}]) { i in var c = CollectionOfOne>(OpaqueValue(42)) expectCrashLater() ${operation} } % end LazyTestSuite.test("CollectionOfOne/index(after:), index(before:)") { let c = CollectionOfOne>(OpaqueValue(42)) expectEqual(1, c.index(after: 0)) expectEqual(0, c.index(before: 1)) } LazyTestSuite.test("CollectionOfOne/subscript(Index)/Get/Set/NoTrap") { var c = CollectionOfOne>(OpaqueValue(42)) c[0] = OpaqueValue(4242) expectEqual(4242, c[0].value) expectEqualSequence([OpaqueValue(4242)], c) { $0.value == $1.value } } % for (name, operation) in [ % ('subscript(Range)/Get', '_ = c[r]'), % ('subscript(Range)/Set', 'c[r] = slice'), % ]: LazyTestSuite.test("CollectionOfOne/${name}/Trap") .forEach(in: [ -1 ..< -1, -1..<0, -1..<1, 1..<2, 2..<2, ] as [Range]) { r in var c = CollectionOfOne>(OpaqueValue(42)) let slice = r.count == 0 ? c[0..<0] : c[0..<1] expectCrashLater() ${operation} } % end LazyTestSuite.test("CollectionOfOne/subscript(Range)/Set/DifferentLength/Trap") .forEach(in: [ 0..<0, 0..<1, 1..<1 ] as [Range]) { r in var c = CollectionOfOne>(OpaqueValue(42)) let slice = r.count == 0 ? c[0..<1] : c[0..<0] expectCrashLater() c[r] = slice } LazyTestSuite.test("CollectionOfOne/subscript(Range)/Get/Set/Empty/NoTrap") { var c = CollectionOfOne>(OpaqueValue(42)) let slice0 = c[0..<0] let slice1 = c[1..<1] checkRandomAccessCollection([], slice0) { $0.value == $1.value } checkRandomAccessCollection([], slice1) { $0.value == $1.value } c[0..<0] = slice0 c[0..<0] = slice1 c[1..<1] = slice0 c[1..<1] = slice1 expectEqualSequence([OpaqueValue(42)], c) { $0.value == $1.value } } LazyTestSuite.test("CollectionOfOne/{CustomDebugStringConvertible,CustomReflectable}") { let c = CollectionOfOne(CustomPrintableValue(42)) expectPrinted("CollectionOfOne((value: 42).debugDescription)", c) expectDebugPrinted("CollectionOfOne((value: 42).debugDescription)", c) expectDumped( "▿ CollectionOfOne((value: 42).debugDescription)\n" + " ▿ element: (value: 42).debugDescription\n" + " - value: 42\n" + " - identity: 0\n", c) } //===----------------------------------------------------------------------===// // EmptyCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension EmptyCollection where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("EmptyCollection") { let c = EmptyCollection>() expectEqual(0, c.startIndex) expectEqual(0, c.endIndex) expectEqual(0..<0, c.indices) checkRandomAccessCollection([], c) { $0.value == $1.value } } LazyTestSuite.test("EmptyCollection/CustomReflectable") { let c = EmptyCollection>() expectPrinted("EmptyCollection>()", c) expectDebugPrinted("Swift.EmptyCollection>()", c) expectDumped( "- Swift.EmptyCollection>\n", c) } LazyTestSuite.test("EmptyCollection/Equatable") { let instances = [ EmptyCollection>() ] checkEquatable(instances, oracle: { $0 == $1 }) } LazyTestSuite.test("EmptyCollection/AssociatedTypes") { typealias Subject = EmptyCollection> expectRandomAccessCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: EmptyIterator>.self, subSequenceType: Subject.self, indexType: Int.self, indexDistanceType: Int.self, indicesType: CountableRange.self) } % for (name, operation) in [ % ('index(after:)', '_ = c.index(after: i)'), % ('index(before:)', '_ = c.index(before: i)'), % ('subscript(Index)/Get', '_ = c[i]'), % ('subscript(Index)/Set', 'c[i] = OpaqueValue(42)'), % ]: LazyTestSuite.test("EmptyCollection/${name}") .forEach(in: [-1, 0, 1]) { i in var c = EmptyCollection>() expectCrashLater() ${operation} } % end % for (name, operation) in [ % ('subscript(Range)/Get', '_ = c[r]'), % ('subscript(Range)/Set', 'c[r] = c'), % ]: LazyTestSuite.test("EmptyCollection/${name}/Trap") .forEach(in: [-1 ..< -1, -1..<0, -1..<1, 0..<1, 1..<1] as [Range]) { r in var c = EmptyCollection>() expectCrashLater() ${operation} } LazyTestSuite.test("EmptyCollection/${name}/NoTrap") { var c = EmptyCollection>() let r: Range = 0..<0 ${operation} expectEqualSequence([], c[0..<0]) { $0.value == $1.value } } % end LazyTestSuite.test("EmptyCollection/index(_:offsetBy:)/Trap") .forEach(in: [ (-1, -1), (-1, 0), (-1, 1), (0, 1), (0, -1), (1, -1), (1, 0), (1, 1), ]) { (i, offset) in let c = EmptyCollection>() expectCrashLater() _ = c.index(i, offsetBy: offset) } LazyTestSuite.test("EmptyCollection/index(_:offsetBy:)/NoTrap") { let c = EmptyCollection>() expectEqual(c.startIndex, c.index(c.startIndex, offsetBy: 0)) } LazyTestSuite.test("EmptyCollection/index(_:offsetBy:limitedBy:)/Trap") .forEach(in: [ (-1, 0, 0), (-1, 1, 0), (0, 0, -1), (0, 0, 1), (1, -1, 0), (1, 0, 0), (1, 0, 1), ]) { (i, offset, limit) in let c = EmptyCollection>() expectCrashLater() _ = c.index(i, offsetBy: offset, limitedBy: limit) } LazyTestSuite.test("EmptyCollection/index(_:offsetBy:limitedBy:)/NoTrap") .forEach(in: [ -10, -1, 0, 1, 10, ]) { offset in let c = EmptyCollection>() let result = c.index(c.startIndex, offsetBy: offset, limitedBy: c.startIndex) if offset == 0 { expectOptionalEqual(c.startIndex, result) } else { expectEmpty(result) } } LazyTestSuite.test("EmptyCollection/distance(from:to:)/Trap") .forEach(in: [ (-1, -1), (-1, 0), (-1, 1), (0, 1), (0, -1), (1, -1), (1, 0), (1, 1), ]) { (start, end) in let c = EmptyCollection>() expectCrashLater() _ = c.distance(from: start, to: end) } LazyTestSuite.test("EmptyCollection/distance(from:to:)/NoTrap") { let c = EmptyCollection>() expectEqual(0, c.distance(from: 0, to: 0)) } LazyTestSuite.test("EmptyCollection/_failEarlyRangeCheck/NoTrap") { let c = EmptyCollection>() c._failEarlyRangeCheck(0, bounds: 0..<0) c._failEarlyRangeCheck(0..<0, bounds: 0..<0) } //===----------------------------------------------------------------------===// // EmptyIterator //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension EmptyIterator where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("EmptyIterator") { checkIterator( [] as Array>, EmptyIterator>()) { $0.value == $1.value } } // FIXME: trap tests. //===----------------------------------------------------------------------===// // lazy //===----------------------------------------------------------------------===// LazyTestSuite.test("isEmpty") { expectTrue((0..<0).lazy.isEmpty) expectFalse((0...0).lazy.isEmpty) } LazyTestSuite.test("first") { expectOptionalEqual(7, (7..<42).lazy.first) } LazyTestSuite.test("first empty") { expectEmpty((7..<7).lazy.first) } LazyTestSuite.test("last") { expectOptionalEqual(41, (7..<42).lazy.last) } LazyTestSuite.test("last empty") { expectEmpty((7..<7).lazy.last) } //===----------------------------------------------------------------------===// // LazySequence //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension LazySequence where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("LazySequence/underestimatedCount") { let s = MinimalSequence( elements: [ 0, 30, 10, 90 ].map(OpaqueValue.init), underestimatedCount: .value(42)) var lazySeq = s.lazy expectType(LazySequence>>.self, &lazySeq) expectEqual(42, lazySeq.underestimatedCount) } % for Traversal in TRAVERSALS: % TraversalCollection = collectionForTraversal(Traversal) LazyTestSuite.test("LazySequence<${TraversalCollection}>/underestimatedCount") { let s = Minimal${TraversalCollection}( elements: [ 0, 30, 10, 90 ].map(OpaqueValue.init), underestimatedCount: .value(42)) var lazySeq = s.lazy expectType( Lazy${TraversalCollection}< Minimal${TraversalCollection}> >.self, &lazySeq) expectEqual(42, lazySeq.underestimatedCount) } % end //===----------------------------------------------------------------------===// // MapSequence //===----------------------------------------------------------------------===// LazyTestSuite.test("MapSequence/underestimatedCount") { let s = MinimalSequence( elements: [ 0, 30, 10, 90 ].map(OpaqueValue.init), underestimatedCount: .value(42)) var lazyMap = s.lazy.map { OpaqueValue(Int32($0.value)) } expectType( LazyMapSequence>, OpaqueValue>.self, &lazyMap) expectEqual(42, lazyMap.underestimatedCount) } struct SequenceWithCustomUnderestimatedCount : Sequence { init(_ data: [Int]) { self._data = MinimalSequence(elements: data.map(OpaqueValue.init)) } func makeIterator() -> MinimalSequence>.Iterator { return _data.makeIterator() } var underestimatedCount: Int { SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled += 1 return _data.underestimatedCount } static var timesUnderestimatedCountWasCalled: Int = 0 let _data: MinimalSequence> } LazyTestSuite.test("LazySequence.array") { SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled = 0 let base = SequenceWithCustomUnderestimatedCount([ 0, 30, 10, 90 ]) expectEqual([ 0, 30, 10, 90 ], base.lazy.map { $0.value }) // Lazy sequences should use underestimated count to preallocate array // storage. expectEqual(1, SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled) expectEqualSequence( [], Array(base).map { $0.value }, "sequence should be consumed") } % for Traversal in TRAVERSALS: % TraversalCollection = collectionForTraversal(Traversal) LazyTestSuite.test("MapCollection<${TraversalCollection}>/underestimatedCount") { let s = Minimal${TraversalCollection}( elements: [ 0, 30, 10, 90 ].map(OpaqueValue.init), underestimatedCount: .value(42)) var lazyMap = s.lazy.map { (input: OpaqueValue) -> OpaqueValue in OpaqueValue(Int32(input.value)) } expectType( LazyMap${TraversalCollection}< Minimal${TraversalCollection}>, OpaqueValue >.self, &lazyMap) expectEqual(42, lazyMap.underestimatedCount) } % end //===----------------------------------------------------------------------===// // LazyCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension LazyCollection where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } %for (Traversal, ReversedType) in [ % ('Forward', None), % ('Bidirectional', 'ReversedCollection'), % ('RandomAccess', 'ReversedRandomAccessCollection') %]: % TraversalCollection = collectionForTraversal(Traversal) LazyTestSuite.test("Lazy${TraversalCollection}.array") { let base = Minimal${TraversalCollection}( elements: [ 0, 30, 10, 90 ], underestimatedCount: .value(42)) let arrayFromLazy = Array(base.lazy) expectEqual([ 0, 30, 10, 90 ], arrayFromLazy) // Lazy collections should not use underestimated count to preallocate array // storage, since they have access to real count instead. expectLE(4, arrayFromLazy.capacity) expectGE(40, arrayFromLazy.capacity) } % if ReversedType is not None: LazyTestSuite.test("Lazy${TraversalCollection}.reversed") { let base = Minimal${TraversalCollection}( elements: [ 0, 30, 10, 90 ].map(OpaqueValue.init), underestimatedCount: .value(42)) var reversed = base.lazy.reversed() expectType( Lazy${TraversalCollection}< ${ReversedType}>> >.self, &reversed) let expected: [OpaqueValue] = [ 90, 10, 30, 0 ].map(OpaqueValue.init) check${Traversal}Collection(expected, reversed) { $0.value == $1.value } var reversedTwice = reversed.reversed() expectType( Lazy${TraversalCollection}< ${ReversedType}<${ReversedType}< Minimal${TraversalCollection}> >>>.self, &reversedTwice) check${Traversal}Collection( [ 0, 30, 10, 90 ].map(OpaqueValue.init) as [OpaqueValue], reversedTwice) { $0.value == $1.value } } % end %end //===----------------------------------------------------------------------===// // ReversedCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReversedCollection where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // ReversedIndex //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReversedIndex where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // RandomAccessReversedCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReversedRandomAccessCollection where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // ReversedRandomAccessIndex //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReversedRandomAccessIndex where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } var tests = TestSuite("NewLazy") tests.test("LazySequence/Sequence") { let expected = (0..<100).map(OpaqueValue.init) var actual = MinimalSequence(elements: expected).lazy expectType( LazySequence>>.self, &actual) // Asking for .lazy again doesn't re-wrap the type var again = actual.lazy expectType( LazySequence>>.self, &again) var elements = actual.elements // Expect .elements to strip a lazy wrapper expectType(MinimalSequence>.self, &elements) checkSequence(expected, actual, resiliencyChecks: .none) { $0.value == $1.value } } func expectSequencePassthrough< S : Sequence, Base : Sequence where S : LazySequenceProtocol, Base : LoggingType, Base.Iterator.Element == S.Iterator.Element >(_ s: S, base: Base, arbitraryElement: S.Iterator.Element, count: Int) { let baseType = base.dynamicType SequenceLog.makeIterator.expectIncrement(baseType) { _ = s.makeIterator() } SequenceLog.underestimatedCount.expectIncrement(baseType) { _ = s.underestimatedCount } SequenceLog._customContainsEquatableElement.expectIncrement(baseType) { _ = s._customContainsEquatableElement(arbitraryElement) } SequenceLog._copyToNativeArrayBuffer.expectIncrement(baseType) { _ = s._copyToNativeArrayBuffer() } SequenceLog._copyContents.expectIncrement(baseType) { () -> Void in let buf = UnsafeMutablePointer(allocatingCapacity: count) let end = s._copyContents(initializing: buf) expectTrue(end <= buf + count) buf.deinitialize(count: end - buf) buf.deallocateCapacity(count) } } tests.test("LazySequence/Passthrough") { // Test that operations that might be optimized are passed // through to the underlying sequence. let a = (0..<100).map(OpaqueValue.init) let base = LoggingSequence(wrapping: a) expectSequencePassthrough( base.lazy, base: base, arbitraryElement: OpaqueValue(0), count: a.count) } % for Traversal in TRAVERSALS: % TraversalCollection = collectionForTraversal(Traversal) tests.test("Lazy${TraversalCollection}/Collection") { let expected = (0..<100).map(OpaqueValue.init) let base = Minimal${TraversalCollection}(elements: expected) var actual = base.lazy expectType(Lazy${TraversalCollection}< Minimal${TraversalCollection}> >.self, &actual) // Asking for .lazy again doesn't re-wrap the type var again = actual.lazy expectType(Lazy${TraversalCollection}< Minimal${TraversalCollection}> >.self, &again) check${Traversal}Collection( expected, base.lazy, resiliencyChecks: .none ) { $0.value == $1.value } var elements = base.lazy.elements expectType(Minimal${TraversalCollection}>.self, &elements) } % end tests.test("LazyCollection/Passthrough") { let expected = (0..<100).map(OpaqueValue.init) let base = LoggingCollection(wrapping: expected) expectSequencePassthrough( base.lazy, base: base.lazy._base, arbitraryElement: OpaqueValue(0), count: Int(expected.count)) let s = base.lazy let baseType = base.dynamicType let startIndex = CollectionLog.startIndex.expectIncrement(baseType) { s.startIndex } let endIndex = CollectionLog.endIndex.expectIncrement(baseType) { s.endIndex } CollectionLog.subscriptIndex.expectIncrement(baseType) { _ = s[startIndex] } CollectionLog.subscriptRange.expectUnchanged(baseType) { _ = s[startIndex..) -> OpaqueValue in calls += 1 return OpaqueValue(Double(x.value) / 2.0) } expectEqual(0, calls) expectType( LazyMapSequence< MinimalSequence>, OpaqueValue>.self, &mapped) let expected = [ 1.0, 1.5, 2.5, 3.5, 5.5 ].map(OpaqueValue.init) checkSequence(expected, mapped, resiliencyChecks: .none) { $0.value == $1.value } expectEqual(expected.count, calls) } tests.test("MapSequence/Passthrough") { let expected = (0..<100).map(OpaqueValue.init) let base = LoggingSequence(wrapping: expected) let mapped = base.lazy.map { OpaqueValue(Double($0.value) / 2.0) } CollectionLog.underestimatedCount.expectIncrement(base.dynamicType) { _ = mapped.underestimatedCount } // Not exactly passthrough because we wrap the result CollectionLog.makeIterator.expectIncrement(base.dynamicType) { _ = mapped.makeIterator() } } % for Traversal in TRAVERSALS: % TraversalCollection = collectionForTraversal(Traversal) tests.test("LazyMap${TraversalCollection}/Collection") { let base = Minimal${TraversalCollection}( elements: [2, 3, 5, 7, 11].map(OpaqueValue.init)).lazy var calls = 0 var mapped = base.map { (x: OpaqueValue) -> OpaqueValue in calls += 1 return OpaqueValue(Double(x.value) / 2.0) } expectEqual(0, calls) expectType( LazyMap${TraversalCollection}< Minimal${TraversalCollection}>, OpaqueValue>.self, &mapped) let expected = [ 1.0, 1.5, 2.5, 3.5, 5.5 ].map(OpaqueValue.init) check${Traversal}Collection(expected, mapped, resiliencyChecks: .none) { $0.value == $1.value } // check${Traversal}Collection makes multiple passes over the input, // so we test that each element was transformed *at least* once. expectLE(expected.count, calls) } %end tests.test("LazyMapCollection/Passthrough") { let expected = (0..<100).map(OpaqueValue.init) let base = LoggingCollection(wrapping: expected) let mapped = base.lazy.map { OpaqueValue(Double($0.value) / 2.0) } let startIndex = CollectionLog.startIndex.expectIncrement(base.dynamicType) { mapped.startIndex } let endIndex = CollectionLog.endIndex.expectIncrement(base.dynamicType) { mapped.endIndex } // Not exactly passthrough, because mapping transforms the result CollectionLog.subscriptIndex.expectIncrement(base.dynamicType) { _ = mapped[startIndex] } CollectionLog.isEmpty.expectIncrement(base.dynamicType) { _ = mapped.isEmpty } CollectionLog.first.expectIncrement(base.dynamicType) { _ = mapped.first } CollectionLog.underestimatedCount.expectIncrement(base.dynamicType) { _ = mapped.underestimatedCount } // Not exactly passthrough because we wrap the result CollectionLog.makeIterator.expectIncrement(base.dynamicType) { _ = mapped.makeIterator() } } tests.test("LazyMapSequence/AssociatedTypes") { typealias Base = MinimalSequence> typealias Subject = LazyMapSequence> expectSequenceAssociatedTypes( sequenceType: Subject.self, iteratorType: LazyMapIterator>.self, subSequenceType: AnySequence>.self) } tests.test("LazyMapCollection/AssociatedTypes") { typealias Base = MinimalCollection> typealias Subject = LazyMapCollection> expectCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyMapIterator>.self, // FIXME(ABI): SubSequence should be `LazyMapCollection`. subSequenceType: Slice.self, indexType: Base.Index.self, indexDistanceType: Base.IndexDistance.self, indicesType: Base.Indices.self) } tests.test("LazyMapBidirectionalCollection/AssociatedTypes") { typealias Base = MinimalBidirectionalCollection> typealias Subject = LazyMapBidirectionalCollection> expectBidirectionalCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyMapIterator>.self, // FIXME(ABI): SubSequence should be `LazyMapBidirectionalCollection`. subSequenceType: BidirectionalSlice.self, indexType: Base.Index.self, indexDistanceType: Base.IndexDistance.self, indicesType: Base.Indices.self) } tests.test("LazyMapRandomAccessCollection/AssociatedTypes") { typealias Base = MinimalRandomAccessCollection> typealias Subject = LazyMapRandomAccessCollection> expectRandomAccessCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyMapIterator>.self, // FIXME(ABI): SubSequence should be `LazyMapRandomAccessCollection`. subSequenceType: RandomAccessSlice.self, indexType: Base.Index.self, indexDistanceType: Base.IndexDistance.self, indicesType: Base.Indices.self) } tests.test("lazy.mapped/TypeInference") { let baseArray: [OpaqueValue] = (0..<10).map(OpaqueValue.init) do { var mapped = MinimalSequence(elements: baseArray) .lazy.map { _ in OpaqueValue(0) } expectType( LazyMapSequence< MinimalSequence>, OpaqueValue >.self, &mapped) } do { var mapped = MinimalCollection(elements: baseArray) .lazy.map { _ in OpaqueValue(0) } expectType( LazyMapCollection< MinimalCollection>, OpaqueValue >.self, &mapped) } do { var mapped = MinimalBidirectionalCollection(elements: baseArray) .lazy.map { _ in OpaqueValue(0) } expectType( LazyMapBidirectionalCollection< MinimalBidirectionalCollection>, OpaqueValue >.self, &mapped) } do { var mapped = MinimalRandomAccessCollection(elements: baseArray) .lazy.map { _ in OpaqueValue(0) } expectType( LazyMapRandomAccessCollection< MinimalRandomAccessCollection>, OpaqueValue >.self, &mapped) } } //===--- Reverse ----------------------------------------------------------===// tests.test("ReversedCollection") { let expected = Array(stride(from: 11, through: 0, by: -1)) let r = 0..<12 checkRandomAccessCollection( expected, r.reversed()) // Check that the reverse collection is still eager do { var calls = 0 _ = r.reversed().map { _ in calls += 1 } expectEqual(r.count, calls) } checkBidirectionalCollection( "raboof".characters, "foobar".characters.reversed()) // Check that the reverse collection is still eager do { var calls = 0 _ = "foobar".characters.reversed().map { _ in calls += 1 } expectEqual("foobar".characters.count, calls) } } enum _Void {} struct ExpectType { static func test(_: T){ print("T") } static func test(_: Any) { fatalError() } static func test(_: Any) -> _Void { fatalError() } } tests.test("ReversedCollection/Lazy") { // Check that reversing a lazy collection, or lazy-ing a reverse // collection, produces the same lazy reverse collection. do { let base = Array(stride(from: 11, through: 0, by: -1)).lazy.map { $0 } typealias Base = LazyMapRandomAccessCollection<[Int], Int> ExpectType.test(base) typealias LazyReversedBase = LazyRandomAccessCollection< ReversedRandomAccessCollection> let reversed = base.reversed() ExpectType.test(reversed) var calls = 0 let reversedAndMapped = reversed.map { (x) -> Int in calls += 1; return x } expectEqual(0, calls) checkRandomAccessCollection(0...11, reversedAndMapped) expectNotEqual(0, calls) } do { typealias Expected = LazyBidirectionalCollection< ReversedCollection > let base = "foobar".characters.lazy.map { $0 } typealias Base = LazyMapBidirectionalCollection< String.CharacterView, Character> ExpectType.test(base) typealias LazyReversedBase = LazyBidirectionalCollection< ReversedCollection> let reversed = base.reversed() ExpectType.test(reversed) var calls = 0 let reversedAndMapped = reversed.map { (x) -> Character in calls += 1; return x } expectEqual(0, calls) checkBidirectionalCollection("raboof".characters, reversedAndMapped) expectNotEqual(0, calls) } } // Given a couple of sequences backed by FilterGenerator's, check that // the first selects even numbers and the second selects odd numbers, // both from an underlying sequence of whole numbers. func checkFilterIteratorBase< S : Sequence, I : IteratorProtocol where S.Iterator == LazyFilterIterator, I.Element == OpaqueValue >(_ s1: S, _ s2: S) { var iter1 = s1.makeIterator() expectEqual(0, iter1.next()!.value) expectEqual(2, iter1.next()!.value) expectEqual(4, iter1.next()!.value) var h1 = iter1.base expectEqual(5, h1.next()!.value) expectEqual(6, h1.next()!.value) expectEqual(7, h1.next()!.value) var iter2 = s2.makeIterator() expectEqual(1, iter2.next()!.value) expectEqual(3, iter2.next()!.value) expectEqual(5, iter2.next()!.value) var h2 = iter2.base expectEqual(6, h2.next()!.value) expectEqual(7, h2.next()!.value) expectEqual(8, h2.next()!.value) } tests.test("LazyFilterSequence") { let base = (0..<100).map(OpaqueValue.init) var calls = 0 var filtered = MinimalSequence(elements: base).lazy.filter { x in calls += 1; return x.value % 2 == 0 } expectEqual(calls, 0, "filtering was eager!") ExpectType< LazyFilterSequence>> >.test(filtered) let evens = stride(from: 0, to: 100, by: 2).map(OpaqueValue.init) checkSequence(evens, filtered, resiliencyChecks: .none) { $0.value == $1.value } expectEqual(100, calls) // Check that it works when the first element doesn't satisfy the predicate let odds = stride(from: 1, to: 100, by: 2).map(OpaqueValue.init) filtered = MinimalSequence(elements: base).lazy.filter { $0.value % 2 != 0 } checkSequence(odds, filtered, resiliencyChecks: .none) { $0.value == $1.value } // Try again using explicit construction filtered = LazyFilterSequence( _base: MinimalSequence(elements: base), whereElementsSatisfy: { x in calls += 1; return x.value % 2 == 0}) expectEqual(100, calls) // Check that it constructs the same sequence checkSequence(evens, filtered, resiliencyChecks: .none) { $0.value == $1.value } expectEqual(200, calls) checkFilterIteratorBase( MinimalSequence(elements: base).lazy.filter { $0.value % 2 == 0 }, MinimalSequence(elements: base).lazy.filter { $0.value % 2 != 0 }) } tests.test("LazyFilterIndex/base") { let base = MinimalCollection(elements: (0..<100).map(OpaqueValue.init)) let evens = base.lazy.filter { $0.value % 2 == 0 } let odds = base.lazy.filter { $0.value % 2 != 0 } expectEqual(base.startIndex, evens.startIndex.base) expectEqual(base.index(after: base.startIndex), odds.startIndex.base) expectEqual( base.index(after: base.index(after: base.startIndex)), evens.index(after: evens.startIndex).base) expectEqual( base.index(after: base.index(after: base.index(after: base.startIndex))), odds.index(after: odds.startIndex).base) } tests.test("LazyFilterCollection") { let base = MinimalCollection(elements: (0..<100).map(OpaqueValue.init)) var calls = 0 let filtered = base.lazy.filter { x in calls += 1; return x.value % 2 == 0 } expectEqual(calls, 0, "filtering was eager!") ExpectType< LazyFilterCollection>> >.test(filtered) checkForwardCollection( stride(from: 0, to: 100, by: 2).map(OpaqueValue.init), filtered, resiliencyChecks: .none ) { $0.value == $1.value } expectGE(calls, 100) let oldCalls = calls _ = filtered.first expectLT(oldCalls, calls) expectGE(oldCalls + 2, calls) checkFilterIteratorBase( base.lazy.filter { $0.value % 2 == 0 }, base.lazy.filter { $0.value % 2 != 0 }) } tests.test("LazyFilterSequence/AssociatedTypes") { typealias Base = MinimalSequence> typealias Subject = LazyFilterSequence expectSequenceAssociatedTypes( sequenceType: Subject.self, iteratorType: LazyFilterIterator.self, subSequenceType: AnySequence>.self) } tests.test("LazyFilterCollection/AssociatedTypes") { typealias Base = MinimalCollection> typealias Subject = LazyFilterCollection expectCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyFilterIterator.self, // FIXME(ABI): SubSequence should be `LazyFilterCollection`. subSequenceType: Slice.self, indexType: LazyFilterIndex.self, indexDistanceType: Base.IndexDistance.self, indicesType: DefaultIndices.self) } tests.test("LazyFilterBidirectionalCollection/AssociatedTypes") { typealias Base = MinimalBidirectionalCollection> typealias Subject = LazyFilterBidirectionalCollection expectBidirectionalCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyFilterIterator.self, // FIXME(ABI): SubSequence should be `LazyFilterBidirectionalCollection`. subSequenceType: BidirectionalSlice.self, indexType: LazyFilterIndex.self, indexDistanceType: Base.IndexDistance.self, indicesType: DefaultBidirectionalIndices.self) } tests.test("lazy.filter/TypeInference") { let baseArray: [OpaqueValue] = (0..<10).map(OpaqueValue.init) do { var filtered = MinimalSequence(elements: baseArray) .lazy.filter { _ in true } expectType( LazyFilterSequence>>.self, &filtered) } do { var filtered = MinimalCollection(elements: baseArray) .lazy.filter { _ in true } expectType( LazyFilterCollection>>.self, &filtered) } do { var filtered = MinimalBidirectionalCollection(elements: baseArray) .lazy.filter { _ in true } expectType( LazyFilterBidirectionalCollection< MinimalBidirectionalCollection> >.self, &filtered) } do { var filtered = MinimalRandomAccessCollection(elements: baseArray) .lazy.filter { _ in true } expectType( LazyFilterBidirectionalCollection< MinimalRandomAccessCollection> >.self, &filtered) } } do { struct Sample { var expected: CountableRange var data: [CountableRange] } let flattenSamples: [Sample] = [ Sample( expected: 0..<8, data: [ 1..<1, 0..<5, 7..<7, 5..<7, 7..<8 ]), Sample(expected: 0..<8, data: [ 0..<5, 7..<7, 5..<7, 7..<8 ]), Sample( expected: 0..<8, data: [ 1..<1, 0..<5, 7..<7, 5..<7, 7..<8, 11..<11 ]), Sample( expected: 0..<16, data: [ 0..<10, 14..<14, 10..<14, 14..<16, 22..<22 ]), Sample(expected: 0..<0, data: [ 11..<11 ]), Sample(expected: 0..<0, data: [ 3..<3, 11..<11 ]), Sample(expected: 0..<0, data: []), ] for sample in flattenSamples { let expected = sample.expected let data = sample.data tests.test("FlattenSequence/\(data)") { var base = MinimalSequence( elements: data.map { MinimalSequence(elements: $0) }) checkSequence(expected, base.flatten(), resiliencyChecks: .none) // Checking that flatten doesn't introduce laziness // checkSequence consumed base, so reassign base = MinimalSequence( elements: data.map { MinimalSequence(elements: $0) }) let flattened = base.flatten() var calls = 0 _ = flattened.map { _ in calls += 1 } expectEqual( expected.count, calls, "unexpected laziness in \(flattened.dynamicType)") } tests.test("FlattenSequence/Lazy/\(data)") { // Checking that flatten doesn't remove laziness let base = MinimalSequence( elements: data.map { MinimalSequence(elements: $0) } ).lazy.map { $0 } let flattened = base.flatten() var calls = 0 _ = flattened.map { _ in calls += 1 } expectEqual(0, calls, "unexpected eagerness in \(flattened.dynamicType)") } % for Traversal in 'Forward', 'Bidirectional': % TraversalCollection = collectionForTraversal(Traversal) tests.test("Flatten${TraversalCollection}/\(data)") { let base = Minimal${TraversalCollection}( elements: data.map { Minimal${TraversalCollection}(elements: $0) }) let flattened = base.flatten() check${Traversal}Collection(expected, flattened, resiliencyChecks: .none) // Checking that flatten doesn't introduce laziness var calls = 0 _ = flattened.map { _ in calls += 1 } expectLE( expected.count, calls, "unexpected laziness in \(flattened.dynamicType)") } tests.test("Flatten${TraversalCollection}/Lazy\(data)") { // Checking that flatten doesn't remove laziness let base = Minimal${TraversalCollection}( elements: data.map { Minimal${TraversalCollection}(elements: $0) } ).lazy.map { $0 } let flattened = base.flatten() var calls = 0 _ = flattened.map { _ in calls += 1 } expectEqual(0, calls, "unexpected eagerness in \(flattened.dynamicType)") } % end } } runAllTests()