//===--- Lazy.swift - Tests for LazySequence and LazyCollection -----------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // RUN: %target-run-simple-swiftgyb // REQUIRES: executable_test // REQUIRES: reflection // With a non-optimized stdlib the test takes very long. // REQUIRES: optimized_stdlib import StdlibUnittest import StdlibCollectionUnittest % from gyb_stdlib_support import TRAVERSALS, collectionForTraversal var LazyTestSuite = TestSuite("Lazy") protocol TestProtocol1 {} //===----------------------------------------------------------------------===// // repeatElement(), Repeated //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension Repeated where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("Repeated/AssociatedTypes") { typealias Subject = Repeated> expectRandomAccessCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: IndexingIterator.self, subSequenceType: Slice.self, indexType: Int.self, indicesType: Range.self) } LazyTestSuite.test("repeatedValue()/TypeInference") { var r = repeatElement(OpaqueValue(42), count: 42) expectType(Repeated>.self, &r) } LazyTestSuite.test("repeatedValue()/NegativeCount") { expectCrashLater() _ = repeatElement(OpaqueValue(42), count: -1) } LazyTestSuite.test("Repeated") .forEach(in: [ 0, 1, 3 ]) { count in let c = repeatElement(OpaqueValue(42), count: count) expectEqual(0, c.startIndex) expectEqual(count, c.endIndex) expectEqual(count, c.count) expectEqual(42, c.repeatedValue.value) let expected = (0..>, CollectionOfOne.Iterator(_elements: nil as Optional>)) { $0.value == $1.value } checkIterator( [ OpaqueValue(42) ] as Array>, CollectionOfOne.Iterator(_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: Subject.Iterator.self, subSequenceType: Slice.self, indexType: Int.self, indicesType: Range.self) } let collectionOfOneIndexTests: [(String,(inout CollectionOfOne>,Int)->Void,[Int])] = [ ("index(after:)", { _ = $0.index(after: $1) }, [-2, -1, 1, 2]), ("index(before:)", { _ = $0.index(before: $1) }, [-1, 0, 2]), ("subscript(Index)/Get", { _ = $0[$1] }, [-2, -1, 1, 2]), ("subscript(Index)/Set", { $0[$1] = OpaqueValue(42) }, [-2, -1, 1, 2]), ] for (name,operation,indices) in collectionOfOneIndexTests { LazyTestSuite.test("CollectionOfOne/\(name)") .forEach(in: indices) { i in var c = CollectionOfOne>(OpaqueValue(42)) expectCrashLater() operation(&c,i) } } 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 } } let collectionOfOneInvalidRanges = [-1 ..< -1, -1..<0, -1..<1, 1..<2, 2..<2] LazyTestSuite.test("CollectionOfOne/subscript(Range)/Get/Trap") .forEach(in: collectionOfOneInvalidRanges) { r in let c = CollectionOfOne>(OpaqueValue(42)) expectCrashLater() _ = c[r] } LazyTestSuite.test("CollectionOfOne/subscript(Range)/Set/Trap") .forEach(in: collectionOfOneInvalidRanges) { r in var c = CollectionOfOne>(OpaqueValue(42)) let slice = r.count == 0 ? c[0..<0] : c[0..<1] expectCrashLater() c[r] = slice } 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: EmptyCollection>.Iterator.self, subSequenceType: Subject.self, indexType: Int.self, indicesType: Range.self) } let emptyCollectionIndexTests: [(String,(inout EmptyCollection>,Int)->Void)] = [ ("index(after:)", { _ = $0.index(after: $1) }), ("index(before:)", { _ = $0.index(before: $1) }), ("subscript(Index)/Get", { _ = $0[$1] }), ("subscript(Index)/Set", { $0[$1] = OpaqueValue(42) }), ] for (name,operation) in emptyCollectionIndexTests { LazyTestSuite.test("EmptyCollection/\(name)") .forEach(in: [-1, 0, 1]) { i in var c = EmptyCollection>() expectCrashLater() operation(&c,i) } } % 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>() // Access is guarded by a _debugPrecondition in EmptyCollection if _isDebugAssertConfiguration() { 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>() // Access is guarded by a _debugPrecondition in EmptyCollection if _isDebugAssertConfiguration() { 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>() // Access is guarded by a _debugPrecondition in EmptyCollection if _isDebugAssertConfiguration() { 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 { expectEqual(c.startIndex, result) } else { expectNil(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>() // Access is guarded by a _debugPrecondition in EmptyCollection if _isDebugAssertConfiguration() { 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>, EmptyCollection>.Iterator()) { $0.value == $1.value } } // FIXME: trap tests. //===----------------------------------------------------------------------===// // lazy //===----------------------------------------------------------------------===// LazyTestSuite.test("isEmpty") { expectTrue((0..<0).lazy.isEmpty) expectFalse((0...0).lazy.isEmpty) } LazyTestSuite.test("first") { expectEqual(7, (7..<42).lazy.first) } LazyTestSuite.test("first empty") { expectNil((7..<7).lazy.first) } LazyTestSuite.test("last") { expectEqual(41, (7..<42).lazy.last) } LazyTestSuite.test("last empty") { expectNil((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( LazyCollection< 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( LazyMapCollection< 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', 'ReversedCollection') %]: % 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( ${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( ${ReversedType}<${ReversedType}> >>>.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 ReversedCollection.Index where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // RandomAccessReversedCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReversedCollection where Base : TestProtocol1, Base : RandomAccessCollection { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // ReversedIndex for RandomAccessCollections //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReversedCollection.Index where Base : TestProtocol1, Base : RandomAccessCollection { 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 : LazySequenceProtocol, Base : Sequence >(_ s: S, base: Base, arbitraryElement: S.Iterator.Element, count: Int) where Base : LoggingType, Base.Iterator.Element == S.Iterator.Element { let baseType = type(of: base) SequenceLog.makeIterator.expectIncrement(baseType) { _ = s.makeIterator() } SequenceLog.underestimatedCount.expectIncrement(baseType) { _ = s.underestimatedCount } SequenceLog._customContainsEquatableElement.expectIncrement(baseType) { _ = s._customContainsEquatableElement(arbitraryElement) } SequenceLog._copyToContiguousArray.expectIncrement(baseType) { _ = s._copyToContiguousArray() } SequenceLog._copyContents.expectIncrement(baseType) { () -> Void in let ptr = UnsafeMutablePointer.allocate(capacity: count) let buf = UnsafeMutableBufferPointer(start: ptr, count: count) var (remainders,writtenUpTo) = s._copyContents(initializing: buf) expectTrue(remainders.next() == nil, "_copyContents returned unwritten elements") expectTrue(writtenUpTo == buf.endIndex, "_copyContents failed to use entire buffer") ptr.deinitialize(count: count) ptr.deallocate() } } 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(LazyCollection< Minimal${TraversalCollection}> >.self, &actual) // Asking for .lazy again doesn't re-wrap the type var again = actual.lazy expectType(LazyCollection< Minimal${TraversalCollection}> >.self, &again) checkOneLevelOf${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.elements, arbitraryElement: OpaqueValue(0), count: Int(expected.count)) let s = base.lazy let baseType = type(of: base) 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(type(of: base)) { _ = mapped.underestimatedCount } // Not exactly passthrough because we wrap the result CollectionLog.makeIterator.expectIncrement(type(of: base)) { _ = 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( LazyMapCollection< 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(type(of: base)) { mapped.startIndex } _ = CollectionLog.endIndex.expectIncrement(type(of: base)) { mapped.endIndex } // Not exactly passthrough, because mapping transforms the result CollectionLog.subscriptIndex.expectIncrement(type(of: base)) { _ = mapped[startIndex] } CollectionLog.isEmpty.expectIncrement(type(of: base)) { _ = mapped.isEmpty } CollectionLog.underestimatedCount.expectIncrement(type(of: base)) { _ = mapped.underestimatedCount } // Not exactly passthrough because we wrap the result CollectionLog.makeIterator.expectIncrement(type(of: base)) { _ = mapped.makeIterator() } } tests.test("LazyMapSequence/AssociatedTypes") { typealias Base = MinimalSequence> typealias Subject = LazyMapSequence> expectSequenceAssociatedTypes( sequenceType: Subject.self, iteratorType: LazyMapSequence>.Iterator.self) } tests.test("LazyMapCollection/AssociatedTypes") { typealias Base = MinimalCollection> typealias Subject = LazyMapCollection> expectCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyMapCollection>.Iterator.self, subSequenceType: LazyMapCollection>.self, indexType: Base.Index.self, indicesType: Base.Indices.self) } tests.test("LazyMapBidirectionalCollection/AssociatedTypes") { typealias Base = MinimalBidirectionalCollection> typealias Subject = LazyMapCollection> expectBidirectionalCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyMapCollection>.Iterator.self, subSequenceType: LazyMapCollection>.self, indexType: Base.Index.self, indicesType: Base.Indices.self) } tests.test("LazyMapRandomAccessCollection/AssociatedTypes") { typealias Base = MinimalRandomAccessCollection> typealias Subject = LazyMapCollection> expectRandomAccessCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyMapCollection>.Iterator.self, subSequenceType: LazyMapCollection>.self, indexType: Base.Index.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( LazyMapCollection< MinimalBidirectionalCollection>, OpaqueValue >.self, &mapped) } do { var mapped = MinimalRandomAccessCollection(elements: baseArray) .lazy.map { _ in OpaqueValue(0) } expectType( LazyMapCollection< 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", "foobar".reversed()) // Check that the reverse collection is still eager do { var calls = 0 _ = "foobar".reversed().map { _ in calls += 1 } expectEqual("foobar".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 = LazyMapCollection<[Int], Int> ExpectType.test(base) typealias LazyReversedBase = ReversedCollection 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 { let base = "foobar".lazy.map { $0 } typealias Base = LazyMapCollection ExpectType.test(base) typealias LazyReversedBase = 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", 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>(_ s1: S, _ s2: S) where S.Iterator == LazyFilterSequence.Iterator, I.Element == OpaqueValue { 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), { 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) expectEqual(base.index(after: base.startIndex), odds.startIndex) expectEqual( base.index(after: base.index(after: base.startIndex)), evens.index(after: evens.startIndex)) expectEqual( base.index(after: base.index(after: base.index(after: base.startIndex))), odds.index(after: odds.startIndex)) } 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) checkOneLevelOfForwardCollection( 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: LazyFilterSequence.Iterator.self) } tests.test("LazyFilterCollection/AssociatedTypes") { typealias Base = MinimalCollection> typealias Subject = LazyFilterCollection expectCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyFilterCollection.Iterator.self, subSequenceType: LazyFilterCollection.self, indexType: Base.Index.self, indicesType: DefaultIndices.self) } tests.test("LazyFilterBidirectionalCollection/AssociatedTypes") { typealias Base = MinimalBidirectionalCollection> typealias Subject = LazyFilterCollection expectBidirectionalCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyFilterCollection.Iterator.self, subSequenceType: LazyFilterCollection.self, indexType: Base.Index.self, indicesType: DefaultIndices.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( LazyFilterCollection< MinimalBidirectionalCollection> >.self, &filtered) } do { var filtered = MinimalRandomAccessCollection(elements: baseArray) .lazy.filter { _ in true } expectType( LazyFilterCollection< MinimalRandomAccessCollection> >.self, &filtered) } } do { struct Sample { var expected: Range var data: [Range] } 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.joined(), 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.joined() var calls = 0 _ = flattened.map { _ in calls += 1 } expectEqual( expected.count, calls, "unexpected laziness in \(type(of: flattened))") } 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.joined() var calls = 0 _ = flattened.map { _ in calls += 1 } expectEqual(0, calls, "unexpected eagerness in \(type(of: flattened))") } % for Traversal in 'Forward', 'Bidirectional': % TraversalCollection = collectionForTraversal(Traversal) tests.test("FlattenCollection/${Traversal}/\(data)") { let base = Minimal${TraversalCollection}( elements: data.map { Minimal${TraversalCollection}(elements: $0) }) let flattened = base.joined() 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 \(type(of: flattened))") } tests.test("FlattenCollection/${Traversal}/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.joined() var calls = 0 _ = flattened.map { _ in calls += 1 } expectEqual(0, calls, "unexpected eagerness in \(type(of: flattened))") } % end } } struct TryFlattenIndex where C.Element: Collection { typealias FlattenedIndex = FlattenCollection.Index } //===--- LazyPrefixWhile --------------------------------------------------===// let prefixDropWhileTests: [(data: [Int], value: Int, pivot: Int)] prefixDropWhileTests = [ ([], 0, 0), ([0], 0, 0), ([0], 99, 1), ([0, 10], 0, 0), ([0, 10], 10, 1), ([0, 10], 99, 2), ([0, 10, 20, 30, 40], 0, 0), ([0, 10, 20, 30, 40], 10, 1), ([0, 10, 20, 30, 40], 20, 2), ([0, 10, 20, 30, 40], 30, 3), ([0, 10, 20, 30, 40], 40, 4), ([0, 10, 20, 30, 40], 99, 5) ] % for Kind in 'Sequence', 'Forward', 'Bidirectional': % Self = 'Sequence' if Kind == 'Sequence' else collectionForTraversal(Kind) % checkKind = 'ForwardCollection' if Kind == 'Forward' else Self tests.test("LazyPrefixWhile${Self}").forEach(in: prefixDropWhileTests) { (data, value, pivot) in let base = Minimal${Self}(elements: data) var calls1 = 0 let prefixed = base.lazy.prefix(while: { calls1 += 1; return $0 != value }) let expected = data.prefix(upTo: pivot) expectEqual(0, calls1) check${checkKind}(expected, prefixed) var calls2 = 0 _ = prefixed.map { _ in calls2 += 1 } expectEqual(0, calls2, "unexpected eagerness in \(type(of: prefixed))") % if Kind == 'Bidirectional': check${checkKind}(expected.reversed(), prefixed.reversed()) % end } % end tests.test("LazyPrefixWhileSequence/AssociatedTypes") { typealias Base = MinimalSequence> typealias Subject = LazyPrefixWhileSequence expectSequenceAssociatedTypes( sequenceType: Subject.self, iteratorType: LazyPrefixWhileSequence.Iterator.self) } tests.test("LazyPrefixWhileCollection/AssociatedTypes") { typealias Base = MinimalCollection> typealias Subject = LazyPrefixWhileCollection expectCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyPrefixWhileSequence.Iterator.self, subSequenceType: Slice.self, indexType: LazyPrefixWhileCollection.Index.self, indicesType: DefaultIndices.self) } tests.test("LazyPrefixWhileBidirectionalCollection/AssociatedTypes") { typealias Base = MinimalBidirectionalCollection> typealias Subject = LazyPrefixWhileCollection expectBidirectionalCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyPrefixWhileCollection.Iterator.self, // FIXME(ABI)#82 (Associated Types with where clauses): SubSequence should be `LazyFilterBidirectionalCollection`. subSequenceType: Slice.self, indexType: LazyPrefixWhileCollection.Index.self, indicesType: DefaultIndices.self) } //===--- LazyDropWhile ----------------------------------------------------===// % for Kind in 'Sequence', 'Forward', 'Bidirectional': % Self = 'Sequence' if Kind == 'Sequence' else collectionForTraversal(Kind) % checkKind = 'ForwardCollection' if Kind == 'Forward' else Self tests.test("LazyDropWhile${Self}").forEach(in: prefixDropWhileTests) { (data, value, pivot) in let base = Minimal${Self}(elements: data) var calls1 = 0 let dropped = base.lazy.drop(while: { calls1 += 1; return $0 != value }) let expected = data.suffix(from: pivot) expectEqual(0, calls1) check${checkKind}(expected, dropped) var calls2 = 0 _ = dropped.map { _ in calls2 += 1 } expectEqual(0, calls2, "unexpected eagerness in \(type(of: dropped))") % if Kind == 'Bidirectional': check${checkKind}(expected.reversed(), dropped.reversed()) % end } % end tests.test("LazyDropWhileSequence/AssociatedTypes") { typealias Base = MinimalSequence> typealias Subject = LazyDropWhileSequence expectSequenceAssociatedTypes( sequenceType: Subject.self, iteratorType: LazyDropWhileSequence.Iterator.self) } tests.test("LazyDropWhileCollection/AssociatedTypes") { typealias Base = MinimalCollection> typealias Subject = LazyDropWhileCollection expectCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyDropWhileSequence.Iterator.self, subSequenceType: Slice.self, indexType: LazyDropWhileCollection.Index.self, indicesType: DefaultIndices.self) } tests.test("LazyDropWhileBidirectionalCollection/AssociatedTypes") { typealias Base = MinimalBidirectionalCollection> typealias Subject = LazyDropWhileCollection expectBidirectionalCollectionAssociatedTypes( collectionType: Subject.self, iteratorType: LazyDropWhileSequence.Iterator.self, // FIXME(ABI)#83 (Associated Types with where clauses): SubSequence should be `LazyFilterBidirectionalCollection`. subSequenceType: Slice.self, indexType: LazyDropWhileCollection.Index.self, indicesType: DefaultIndices.self) } runAllTests()