//===--- 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: rm -rf %t // RUN: mkdir -p %t // RUN: %S/../../utils/gyb %s -o %t/Lazy.swift // RUN: %S/../../utils/line-directive %t/Lazy.swift -- %target-build-swift %t/Lazy.swift -o %t/a.out // RUN: %S/../../utils/line-directive %t/Lazy.swift -- %target-run %t/a.out // REQUIRES: executable_test import StdlibUnittest import StdlibCollectionUnittest // Also import modules which are used by StdlibUnittest internally. This // workaround is needed to link all required libraries in case we compile // StdlibUnittest with -sil-serialize-all. #if _runtime(_ObjC) import ObjectiveC #endif 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. //===----------------------------------------------------------------------===// // CollectionOfOne //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension CollectionOfOne where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("CollectionOfOne") { checkRandomAccessCollection( [ OpaqueValue(42) ], CollectionOfOne(OpaqueValue(42))) { $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 } } //===----------------------------------------------------------------------===// // EmptyCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Element'. extension EmptyCollection where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } LazyTestSuite.test("EmptyCollection") { checkRandomAccessCollection( [], EmptyCollection>()) { $0.value == $1.value } } // FIXME: trap tests. //===----------------------------------------------------------------------===// // 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("firstLast") { expectOptionalEqual(7 as Int, (7..<42).lazy.first) expectOptionalEqual(41 as Int, (7..<42).lazy.last) expectEmpty((7..<7).lazy.first) 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 [ 'Forward', 'Bidirectional', 'RandomAccess' ]: LazyTestSuite.test("LazySequence<${traversal}Collection>/underestimatedCount") { let s = Minimal${traversal}Collection( elements: [ 0, 30, 10, 90 ].map(OpaqueValue.init), underestimatedCount: .value(42)) var lazySeq = s.lazy expectType( LazyCollection< Minimal${traversal}Collection> >.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 ]) let lazySequence = base.lazy let arrayFromLazy = Array(lazySequence) expectEqual([ 0, 30, 10, 90 ], arrayFromLazy.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 [ 'Forward', 'Bidirectional', 'RandomAccess' ]: LazyTestSuite.test("MapCollection<${traversal}Collection>/underestimatedCount") { let s = Minimal${traversal}Collection( 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${traversal}Collection>, 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', 'ReverseCollection'), % ('RandomAccess', 'ReverseRandomAccessCollection') %]: LazyTestSuite.test("LazyCollection.array") { let base = Minimal${traversal}Collection( elements: [ 0, 30, 10, 90 ], underestimatedCount: .value(42)) let lazyCollection = base.lazy let arrayFromLazy = Array(lazyCollection) 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("LazyCollection.reversed") { let base = Minimal${traversal}Collection( elements: [ 0, 30, 10, 90 ].map(OpaqueValue.init), underestimatedCount: .value(42)) let lazyCollection = base.lazy var reversed = lazyCollection.reversed() expectType( LazyCollection<${ReversedType}>>>.self, &reversed) check${traversal}Collection( [ 90, 10, 30, 0 ].map(OpaqueValue.init) as [OpaqueValue], reversed) { $0.value == $1.value } var reversedTwice = reversed.reversed() expectType( LazyCollection<${ReversedType}<${ReversedType}>>>>.self, &reversedTwice) check${traversal}Collection( [ 0, 30, 10, 90 ].map(OpaqueValue.init) as [OpaqueValue], reversedTwice) { $0.value == $1.value } } % end %end //===----------------------------------------------------------------------===// // ReverseCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReverseCollection where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // ReverseIndex //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReverseIndex where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // RandomAccessReverseCollection //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReverseRandomAccessCollection where Base : TestProtocol1 { var _baseIsTestProtocol1: Bool { fatalError("not implemented") } } //===----------------------------------------------------------------------===// // ReverseRandomAccessIndex //===----------------------------------------------------------------------===// // Check that the generic parameter is called 'Base'. extension ReverseRandomAccessIndex 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(a) expectSequencePassthrough( base.lazy, base: base, arbitraryElement: OpaqueValue(0), count: a.count) } % for traversal in 'Forward', 'Bidirectional', 'RandomAccess': tests.test("LazyCollection/Collection/${traversal}") { let expected = (0..<100).map(OpaqueValue.init) let base = Minimal${traversal}Collection(elements: expected) var actual = base.lazy expectType( LazyCollection>>.self, &actual) // Asking for .lazy again doesn't re-wrap the type var again = actual.lazy expectType( LazyCollection>>.self, &again) check${traversal}Collection( expected, base.lazy, resiliencyChecks: .none ) { $0.value == $1.value } var elements = base.lazy.elements expectType(Minimal${traversal}Collection>.self, &elements) } % end tests.test("LazyCollection/Passthrough") { let expected = (0..<100).map(OpaqueValue.init) let base = LoggingCollection(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(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 'Forward', 'Bidirectional', 'RandomAccess': tests.test("LazyMapCollection/${traversal}") { let base = Minimal${traversal}Collection( 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${traversal}Collection>, 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(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() } } //===--- Reverse ----------------------------------------------------------===// tests.test("ReverseCollection") { 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("ReverseCollection/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 = LazyCollection< ReverseRandomAccessCollection> 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 = LazyCollection< ReverseCollection > let base = "foobar".characters.lazy.map { $0 } typealias Base = LazyMapCollection ExpectType.test(base) typealias LazyReversedBase = LazyCollection< ReverseCollection> 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 = MinimalForwardCollection(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.startIndex.successor(), odds.startIndex.base) expectEqual( base.startIndex.successor().successor(), evens.startIndex.successor().base) expectEqual( base.startIndex.successor().successor().successor(), odds.startIndex.successor().base) } tests.test("LazyFilterCollection") { let base = MinimalForwardCollection(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 }) } 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.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': % t = '' if traversal == 'Forward' else traversal tests.test("Flatten${t}Collection/\(data)") { let base = Minimal${traversal}Collection( elements: data.map { Minimal${traversal}Collection(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${t}Collection/Lazy\(data)") { // Checking that flatten doesn't remove laziness let base = Minimal${traversal}Collection( elements: data.map { Minimal${traversal}Collection(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()