| // RUN: %target-run-simple-swiftgyb |
| // REQUIRES: executable_test |
| |
| import StdlibUnittest |
| import StdlibCollectionUnittest |
| |
| let CopyToNativeArrayBufferTests = TestSuite("CopyToNativeArrayBufferTests") |
| |
| CopyToNativeArrayBufferTests.test("Sequence._copyToContiguousArray()") { |
| do { |
| // Call from a static context. |
| let s = |
| MinimalSequence(elements: LifetimeTracked(10)..<LifetimeTracked(27)) |
| |
| expectEqual(0, s.timesMakeIteratorCalled.value) |
| let copy = s._copyToContiguousArray() |
| expectEqual(1, s.timesMakeIteratorCalled.value) |
| expectEqualSequence( |
| Array(10..<27), |
| copy.map { $0.value }) |
| } |
| do { |
| // Call from a generic context. |
| let wrapped = MinimalSequence(elements: LifetimeTracked(10)..<LifetimeTracked(27)) |
| let s = LoggingSequence(wrapping: wrapped) |
| |
| expectEqual(0, wrapped.timesMakeIteratorCalled.value) |
| let copy = s._copyToContiguousArray() |
| expectEqual(1, wrapped.timesMakeIteratorCalled.value) |
| expectEqualSequence( |
| Array(10..<27), |
| copy.map { $0.value }) |
| } |
| } |
| |
| CopyToNativeArrayBufferTests.test("Collection._copyToContiguousArray()") { |
| // Check that collections are handled with the collection-specific API. This |
| // means that we are calling the right default implementation (one for |
| // collections, not the one for sequences). |
| |
| do { |
| // Call from a static context. |
| let c = |
| DefaultedCollection(elements: LifetimeTracked(10)..<LifetimeTracked(27)) |
| |
| expectEqual(0, c.timesMakeIteratorCalled.value) |
| expectEqual(0, c.timesStartIndexCalled.value) |
| let copy = c._copyToContiguousArray() |
| expectEqual(0, c.timesMakeIteratorCalled.value) |
| expectNotEqual(0, c.timesStartIndexCalled.value) |
| expectEqualSequence( |
| Array(10..<27), |
| copy.map { $0.value }) |
| } |
| do { |
| // Call from a generic context. |
| let wrapped = |
| DefaultedCollection(elements: LifetimeTracked(10)..<LifetimeTracked(27)) |
| let s = LoggingSequence(wrapping: wrapped) |
| expectEqual(0, wrapped.timesMakeIteratorCalled.value) |
| expectEqual(0, wrapped.timesStartIndexCalled.value) |
| let copy = s._copyToContiguousArray() |
| expectEqual(0, wrapped.timesMakeIteratorCalled.value) |
| expectNotEqual(0, wrapped.timesStartIndexCalled.value) |
| |
| expectEqualSequence( |
| Array(10..<27), |
| copy.map { $0.value }) |
| } |
| } |
| |
| %{ |
| all_array_types = ['ContiguousArray', 'ArraySlice', 'Array'] |
| }% |
| |
| %for Self in all_array_types: |
| extension ${Self} { |
| typealias _BufferID = UnsafeRawPointer? |
| var _bufferID: _BufferID { |
| return unsafeBitCast(_owner, to: _BufferID.self) |
| } |
| } |
| %end |
| |
| var ArrayTestSuite = TestSuite("Array") |
| |
| ArrayTestSuite.test("sizeof") { |
| var a = [ 10, 20, 30 ] |
| #if arch(i386) || arch(arm) |
| expectEqual(4, MemoryLayout._ofInstance(a).size) |
| #else |
| expectEqual(8, MemoryLayout._ofInstance(a).size) |
| #endif |
| } |
| |
| ArrayTestSuite.test("valueDestruction") { |
| var a = [LifetimeTracked]() |
| for i in 100...110 { |
| a.append(LifetimeTracked(i)) |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Native array tests |
| // FIXME: incomplete. |
| //===----------------------------------------------------------------------===// |
| |
| ArrayTestSuite.test("Native/count/empty") { |
| let a = [LifetimeTracked]() |
| expectEqual(0, a.count) |
| } |
| |
| ArrayTestSuite.test("Native/count") { |
| let a = [ LifetimeTracked(10), LifetimeTracked(20), LifetimeTracked(30) ] |
| expectEqual(3, a.count) |
| } |
| |
| ArrayTestSuite.test("Native/isEmpty/empty") { |
| let a = [LifetimeTracked]() |
| expectTrue(a.isEmpty) |
| } |
| |
| ArrayTestSuite.test("Native/isEmpty") { |
| let a = [ LifetimeTracked(10), LifetimeTracked(20), LifetimeTracked(30) ] |
| expectFalse(a.isEmpty) |
| } |
| |
| protocol TestProtocol1 {} |
| |
| % for array_type in all_array_types: |
| |
| // Check that the generic parameter is called 'Element'. |
| extension ${array_type} where Element : TestProtocol1 { |
| var _elementIsTestProtocol1: Bool { |
| fatalError("not implemented") |
| } |
| } |
| |
| /// Returns an ${array_type} that does not share its buffer with other arrays. |
| func getFresh${array_type}<S : Sequence>(_ sequence: S) |
| -> ${array_type}<S.Iterator.Element> { |
| var result: ${array_type}<S.Iterator.Element> = [] |
| result.reserveCapacity(sequence.underestimatedCount) |
| for element in sequence { |
| result.append(element) |
| } |
| return result |
| } |
| |
| % end |
| |
| enum EnumWithoutPayloads : Equatable { |
| case A, B, C, D |
| } |
| |
| func == (lhs: EnumWithoutPayloads, rhs: EnumWithoutPayloads) -> Bool { |
| switch (lhs, rhs) { |
| case (.A, .A), (.B, .B), (.C, .C), (.D, .D): |
| return true |
| |
| default: |
| return false |
| } |
| } |
| |
| struct SequenceWithCustomUnderestimatedCount : Sequence { |
| init(_ data: [Int]) { |
| self._data = MinimalSequence(elements: data.map(OpaqueValue.init)) |
| } |
| |
| func makeIterator() -> MinimalSequence<OpaqueValue<Int>>.Iterator { |
| return _data.makeIterator() |
| } |
| |
| var underestimatedCount: Int { |
| SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled += 1 |
| return _data.underestimatedCount |
| } |
| |
| static var timesUnderestimatedCountWasCalled: Int = 0 |
| |
| let _data: MinimalSequence<OpaqueValue<Int>> |
| } |
| |
| % for array_type in all_array_types: |
| |
| ArrayTestSuite.test("${array_type}/init(Sequence)") { |
| let base = SequenceWithCustomUnderestimatedCount( |
| [ 0, 30, 10, 90 ]) |
| |
| SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled = 0 |
| |
| let result = ${array_type}(base) |
| |
| expectEqual([ 0, 30, 10, 90 ], result.map { $0.value }) |
| |
| expectEqual(1, SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled) |
| |
| expectEqualSequence( |
| [], Array(base).map { $0.value }, "sequence should be consumed") |
| } |
| |
| ArrayTestSuite.test("${array_type}/Sliceable/Enums") { |
| typealias E = EnumWithoutPayloads |
| |
| do { |
| let expected = [E.A, E.B, E.C, E.D] |
| let sliceable = ${array_type}(expected) |
| checkSliceableWithBidirectionalIndex(expected, sliceable) |
| } |
| |
| /* |
| FIXME: add this test when Array<T> can be conditionally Equatable. |
| do { |
| let expected = [[E.A, E.B], [E.B, E.C], [E.D], [E.A, E.B, E.D]] |
| let sliceable = ${array_type}(expected) |
| checkSliceableWithBidirectionalIndex( |
| expected, sliceable, SourceLocStack().withCurrentLoc()) |
| } |
| */ |
| } |
| |
| ArrayTestSuite.test("${array_type}/appendNonUnique") { |
| var x: ${array_type}<Int> = [] |
| x.reserveCapacity(10002) |
| let capacity = x.capacity |
| for _ in 1...10000 { |
| let y = x |
| x.append(1) |
| expectTrue(x.capacity == capacity) |
| let z = x |
| x.remove(at: 0) |
| } |
| } |
| |
| ArrayTestSuite.test("${array_type}/emptyAllocation") { |
| let arr0 = ${array_type}<Int>() |
| let arr1 = ${array_type}<LifetimeTracked>(repeating: LifetimeTracked(0), count: 0) |
| // Empty arrays all use the same buffer |
| expectEqual(arr0._bufferID, arr1._bufferID) |
| |
| let arr2: ${array_type}<LifetimeTracked> = [] |
| let emptyLiteralsShareBuffer = arr0._bufferID == arr2._bufferID |
| expectTrue(emptyLiteralsShareBuffer) |
| } |
| |
| ArrayTestSuite.test("${array_type}/filter") { |
| do { |
| let arr: ${array_type}<Int> = [] |
| var result = arr.filter() { |
| (x: Int) -> Bool in |
| expectUnreachable() |
| return true |
| } |
| expectType(Array<Int>.self, &result) |
| expectEqual([], result) |
| expectEqual(0, result.capacity) |
| } |
| do { |
| let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ] |
| let result = arr.filter() { (x: Int) -> Bool in true } |
| expectEqual([ 0, 30, 10, 90 ], result) |
| expectGE(2 * result.count, result.capacity) |
| } |
| do { |
| let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ] |
| let result = arr.filter() { (x: Int) -> Bool in false } |
| expectEqual([], result) |
| expectGE(2 * result.count, result.capacity) |
| } |
| do { |
| let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ] |
| let result = arr.filter() { $0 % 3 == 0 } |
| expectEqual([ 0, 30, 90 ], result) |
| expectGE(2 * result.count, result.capacity) |
| } |
| } |
| |
| ArrayTestSuite.test("${array_type}/map") { |
| do { |
| let arr: ${array_type}<Int> = [] |
| var result = arr.map() { |
| (x: Int) -> Int16 in |
| expectUnreachable() |
| return 42 |
| } |
| expectType(Array<Int16>.self, &result) |
| expectEqual([], result) |
| expectEqual(0, result.capacity) |
| } |
| do { |
| let arr: ${array_type}<Int> = [ 0, 30, 10, 90 ] |
| let result = arr.map() { $0 + 1 } |
| expectEqual([ 1, 31, 11, 91 ], result) |
| expectGE(2 * result.count, result.capacity) |
| } |
| } |
| |
| ArrayTestSuite.test("${array_type}/flatMap") { |
| let enumerate : (Int) -> ${array_type}<Int> = |
| { return ${array_type}(1..<($0 + 1)) } |
| expectEqualSequence([], ${array_type}().flatMap(enumerate)) |
| expectEqualSequence([ 1 ], ${array_type}([ 1 ]).flatMap(enumerate)) |
| expectEqualSequence( |
| [ 1, 1, 2 ], |
| ${array_type}([ 1, 2 ]).flatMap(enumerate)) |
| expectEqualSequence( |
| [ 1, 1, 1, 2 ], |
| ${array_type}([ 1, 2 ]).flatMap(enumerate).flatMap(enumerate)) |
| } |
| |
| ArrayTestSuite.test("${array_type}/Mirror") { |
| do { |
| let input: ${array_type}<Int> = [] |
| var output = "" |
| dump(input, to: &output) |
| |
| let expected = |
| "- 0 elements\n" |
| |
| expectEqual(expected, output) |
| } |
| do { |
| let input: ${array_type}<Int> = [ 10, 20, 30, 40 ] |
| var output = "" |
| dump(input, to: &output) |
| |
| let expected = |
| "â–¿ 4 elements\n" + |
| " - 10\n" + |
| " - 20\n" + |
| " - 30\n" + |
| " - 40\n" |
| |
| expectEqual(expected, output) |
| } |
| % if array_type == 'ArraySlice': |
| do { |
| let base = [ 10, 20, 30, 40 ] |
| let input: ArraySlice<Int> = base[1..<3] |
| var output = "" |
| dump(input, to: &output) |
| |
| let expected = |
| "â–¿ 2 elements\n" + |
| " - 20\n" + |
| " - 30\n" |
| |
| expectEqual(expected, output) |
| } |
| % end |
| } |
| |
| % end |
| |
| % for Kind in ['Array', 'ContiguousArray']: |
| ArrayTestSuite.test("${Kind}/popLast") { |
| // Empty |
| do { |
| var a = ${Kind}<Int>() |
| let popped = a.popLast() |
| expectEmpty(popped) |
| expectTrue(a.isEmpty) |
| } |
| |
| do { |
| var popped = [Int]() |
| var a: ${Kind}<Int> = [1010, 2020, 3030] |
| while let element = a.popLast() { |
| popped.append(element) |
| } |
| expectEqualSequence([1010, 2020, 3030], popped.reversed()) |
| expectTrue(a.isEmpty) |
| } |
| } |
| % end |
| |
| // Check how removeFirst() affects indices. |
| % for Kind in ['Array', 'ContiguousArray']: |
| ArrayTestSuite.test("${Kind}/removeFirst") { |
| do { |
| var a: ${Kind}<OpaqueValue<Int>> = ${Kind}([ 1 ].map(OpaqueValue.init)) |
| a.removeFirst() |
| expectEqual(0, a.startIndex) |
| } |
| do { |
| var a: ${Kind}<OpaqueValue<Int>> = ${Kind}([ 1, 2 ].map(OpaqueValue.init)) |
| a.removeFirst() |
| expectEqual(0, a.startIndex) |
| } |
| } |
| % end |
| |
| ArrayTestSuite.test("ArraySlice/removeFirst") { |
| do { |
| let a: [OpaqueValue<Int>] = [ 99, 1010, 99 ].map(OpaqueValue.init) |
| var s = a[1..<2] |
| expectEqual(1, s.startIndex) |
| s.removeFirst() |
| expectEqual(2, s.startIndex) |
| } |
| do { |
| let a: [OpaqueValue<Int>] = [ 99, 1010, 2020, 99 ].map(OpaqueValue.init) |
| var s = a[1..<2] |
| expectEqual(1, s.startIndex) |
| s.removeFirst() |
| expectEqual(2, s.startIndex) |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // _withUnsafeMutableBufferPointerIfSupported() |
| //===----------------------------------------------------------------------===// |
| |
| struct WithUnsafeMutableBufferPointerIfSupportedTest { |
| let sequence: [Int] |
| let loc: SourceLoc |
| |
| init( |
| _ sequence: [Int], |
| file: String = #file, line: UInt = #line |
| ) { |
| self.sequence = sequence |
| self.loc = SourceLoc(file, line, comment: "test data") |
| } |
| } |
| |
| let withUnsafeMutableBufferPointerIfSupportedTests = [ |
| WithUnsafeMutableBufferPointerIfSupportedTest([]), |
| WithUnsafeMutableBufferPointerIfSupportedTest([ 10 ]), |
| WithUnsafeMutableBufferPointerIfSupportedTest([ 10, 20, 30, 40, 50 ]), |
| ] |
| |
| % for array_type in all_array_types: |
| |
| ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported") { |
| for test in withUnsafeMutableBufferPointerIfSupportedTests { |
| var a = getFresh${array_type}(test.sequence.map(OpaqueValue.init)) |
| do { |
| // Read. |
| var result = a._withUnsafeMutableBufferPointerIfSupported { |
| (baseAddress, count) -> OpaqueValue<[OpaqueValue<Int>]> in |
| let bufferPointer = |
| UnsafeMutableBufferPointer(start: baseAddress, count: count) |
| return OpaqueValue(Array(bufferPointer)) |
| } |
| expectType(Optional<OpaqueValue<Array<OpaqueValue<Int>>>>.self, &result) |
| expectEqualSequence(test.sequence, result!.value.map { $0.value }) |
| expectEqualSequence(test.sequence, a.map { $0.value }) |
| } |
| do { |
| // Read and write. |
| var result = a._withUnsafeMutableBufferPointerIfSupported { |
| (baseAddress, count) -> OpaqueValue<Array<OpaqueValue<Int>>> in |
| let bufferPointer = |
| UnsafeMutableBufferPointer(start: baseAddress, count: count) |
| let result = OpaqueValue(Array(bufferPointer)) |
| for i in bufferPointer.indices { |
| bufferPointer[i] = OpaqueValue(bufferPointer[i].value * 10) |
| } |
| return result |
| } |
| expectType(Optional<OpaqueValue<Array<OpaqueValue<Int>>>>.self, &result) |
| expectEqualSequence(test.sequence, result!.value.map { $0.value }) |
| expectEqualSequence( |
| test.sequence.map { $0 * 10 }, |
| a.map { $0.value }) |
| } |
| } |
| // FIXME: tests for arrays bridged from Objective-C. |
| } |
| |
| ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported/ReplacingTheBufferTraps/1") { |
| var a = getFresh${array_type}([ OpaqueValue(10) ]) |
| var result = a._withUnsafeMutableBufferPointerIfSupported { |
| (baseAddress, count) -> OpaqueValue<Int> in |
| // buffer = UnsafeMutableBufferPointer(start: buffer.baseAddress, count: 0) |
| // FIXME: does not trap since the buffer is not passed inout. |
| // expectCrashLater() |
| return OpaqueValue(42) |
| } |
| } |
| |
| ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported/ReplacingTheBufferTraps/2") { |
| var a = getFresh${array_type}([ OpaqueValue(10) ]) |
| var result = a._withUnsafeMutableBufferPointerIfSupported { |
| (baseAddress, count) -> OpaqueValue<Int> in |
| // buffer = UnsafeMutableBufferPointer(start: nil, count: 1) |
| // FIXME: does not trap since the buffer is not passed inout. |
| // expectCrashLater() |
| return OpaqueValue(42) |
| } |
| } |
| |
| //===--- |
| // Check that iterators traverse a snapshot of the collection. |
| //===--- |
| |
| ArrayTestSuite.test( |
| "${array_type}/mutationDoesNotAffectIterator/subscript/store") { |
| var arr: ${array_type}<Int> = [ 1010, 1020, 1030 ] |
| var iter = arr.makeIterator() |
| arr[0] = 1011 |
| expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter))) |
| } |
| |
| ArrayTestSuite.test( |
| "${array_type}/mutationDoesNotAffectIterator/subscript/append") { |
| var arr: ${array_type}<Int> = [ 1010, 1020, 1030 ] |
| var iter = arr.makeIterator() |
| arr.append(1040) |
| expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter))) |
| } |
| |
| ArrayTestSuite.test( |
| "${array_type}/mutationDoesNotAffectIterator/subscript/replaceSubrange") { |
| var arr: ${array_type}<Int> = [ 1010, 1020, 1030 ] |
| var iter = arr.makeIterator() |
| arr.replaceSubrange(1..<3, with: [ 1040, 1050, 1060 ]) |
| expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter))) |
| } |
| |
| % end |
| |
| //===----------------------------------------------------------------------===// |
| // Array and EvilCollection that changes its size while we are not looking |
| //===----------------------------------------------------------------------===// |
| |
| let evilBoundsError = "EvilCollection: index out of range" |
| |
| final class EvilSequence : Sequence { |
| init(_ growth: Int) { |
| self.growth = growth |
| } |
| |
| var growth: Int |
| var _count: Int = 20 |
| |
| var underestimatedCount: Int { |
| defer { _count += growth } |
| return _count |
| } |
| |
| func makeIterator() -> AnyIterator<LifetimeTracked> { |
| var i = 0 |
| return AnyIterator { |
| if i >= self._count { return nil } |
| let result = LifetimeTracked(i) |
| i += 1 |
| return result |
| } |
| } |
| } |
| |
| final class EvilCollection : Collection { |
| func index(after i: Int) -> Int { |
| return i + 1 |
| } |
| |
| init(_ growth: Int, boundsChecked: Bool) { |
| self.growth = growth |
| self.boundsChecked = boundsChecked |
| } |
| |
| var growth: Int |
| var _count: Int = 20 |
| var boundsChecked: Bool |
| |
| var startIndex : Int { |
| _count += growth |
| return 0 |
| } |
| |
| var endIndex : Int { |
| return _count |
| } |
| |
| subscript(i: Int) -> LifetimeTracked { |
| if boundsChecked { |
| precondition(i >= 0 && i < _count, evilBoundsError) |
| } |
| return LifetimeTracked(i) |
| } |
| |
| // Default implementation will call _failEarlyRangeCheck, |
| // passing in a startIndex that will grow _count faster than |
| // necessary. |
| func formIndex(after i: inout Int) { |
| i += 1 |
| } |
| } |
| |
| for (step, evilBoundsCheck) in [ (1, true), (-1, false), (-1, true) ] { |
| |
| let message = step < 0 && evilBoundsCheck |
| ? evilBoundsError |
| : "invalid Collection: count differed in successive traversals" |
| |
| let constructionMessage = |
| /*_isStdlibInternalChecksEnabled() && !evilBoundsCheck && step <= 0 |
| ? "_UnsafePartiallyInitializedContiguousArrayBuffer has no more capacity" |
| :*/ message |
| |
| // The invalid Collection error is a _debugPreconditon that will only fire |
| // in a Debug assert configuration. |
| let expectedToFail = (step < 0 && evilBoundsCheck) || |
| _isDebugAssertConfiguration() |
| |
| let natureOfEvil = step > 0 ? "Growth" : "Shrinkage" |
| let boundsChecked = evilBoundsCheck ? "BoundsChecked" : "NoBoundsCheck" |
| let testPrefix = "MemorySafety/\(boundsChecked)/Evil\(natureOfEvil)" |
| |
| ArrayTestSuite.test("\(testPrefix)/Infrastructure/EvilSequence") { |
| let evil = EvilSequence(step) |
| let count0 = evil.underestimatedCount |
| let count1 = evil.underestimatedCount |
| expectNotEqual(count0, count1) |
| if step > 0 { |
| expectLE(count0, count1) |
| } |
| else { |
| expectGE(count0, count1) |
| } |
| } |
| |
| let t1 = ArrayTestSuite.test("\(testPrefix)/Infrastructure/EvilCollection") |
| (evilBoundsCheck && _isDebugAssertConfiguration() |
| ? t1.crashOutputMatches(evilBoundsError) : t1) |
| .code { |
| let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) |
| let count0 = evil.count |
| let count1 = evil.count |
| expectNotEqual(count0, count1) |
| if step > 0 { |
| expectLE(count0, count1) |
| } |
| else { |
| expectGE(count0, count1) |
| } |
| if evilBoundsCheck { |
| expectCrashLater() |
| } |
| let x = evil[-1] |
| _blackHole(x) |
| } |
| |
| let t2 = ArrayTestSuite.test("\(testPrefix)/Construction") |
| (_isDebugAssertConfiguration() && expectedToFail |
| ? t2.crashOutputMatches(constructionMessage) : t2) |
| .code { |
| let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) |
| |
| if expectedToFail { |
| expectCrashLater() |
| } |
| |
| let a = Array(evil) |
| _blackHole(a) |
| } |
| |
| for (op, rangeMax) in ["Grow":0, "Shrink":200] { |
| let t3 = ArrayTestSuite.test("\(testPrefix)/replaceSubrange/\(op)Unique") |
| (_isDebugAssertConfiguration() ? t3.crashOutputMatches(message) : t3) |
| .code { |
| let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) |
| var a = Array((0..<200).lazy.map { LifetimeTracked($0) }) |
| if expectedToFail { |
| expectCrashLater() |
| } |
| a.replaceSubrange(0..<rangeMax, with: evil) |
| } |
| |
| let t4 = ArrayTestSuite.test("\(testPrefix)/replaceSubrange/\(op)NonUnique") |
| (_isDebugAssertConfiguration() ? t4.crashOutputMatches(message) : t4) |
| .code { |
| let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) |
| var a = Array((0..<200).lazy.map { LifetimeTracked($0) }) |
| var b = a |
| if expectedToFail { |
| expectCrashLater() |
| } |
| a.replaceSubrange(0..<rangeMax, with: evil) |
| _fixLifetime(b) |
| } |
| } |
| |
| ArrayTestSuite.test("\(testPrefix)/SequenceMap") |
| .skip(.custom( |
| { _isFastAssertConfiguration() }, |
| reason: "this trap is not guaranteed to happen in -Ounchecked")) |
| .code { |
| let evil = EvilSequence(step) |
| |
| if step < 0 { |
| expectCrashLater() |
| } |
| let a = evil.map { $0 } |
| _blackHole(a) |
| } |
| |
| ArrayTestSuite.test("\(testPrefix)/CollectionMap") |
| .code { |
| let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) |
| |
| if expectedToFail { |
| expectCrashLater() |
| } |
| |
| let a = evil.map { $0 } |
| _blackHole(a) |
| } |
| |
| ArrayTestSuite.test("\(testPrefix)/FilterAll") |
| .code { |
| let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) |
| |
| let a = evil.filter { _ in true } |
| _blackHole(a) |
| } |
| |
| ArrayTestSuite.test("\(testPrefix)/FilterNone") |
| .code { |
| let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) |
| |
| let a = evil.filter { _ in false } |
| _blackHole(a) |
| } |
| } |
| |
| |
| //===----------------------------------------------------------------------===// |
| // Special cases and one-off tests. |
| //===----------------------------------------------------------------------===// |
| |
| % for array_type in all_array_types: |
| |
| ArrayTestSuite.test("${array_type}<Void>/map") { |
| // This code used to crash because it generated an array of Void with |
| // stride == 0. |
| do { |
| let input: ${array_type}<Void> = [ (), (), () ] |
| let result = input.map { (_) -> Void in return () } |
| expectEqual(3, result.count) |
| } |
| |
| do { |
| let input: ${array_type}<OpaqueValue<Int>> = [ |
| OpaqueValue(10), OpaqueValue(20), OpaqueValue(30) |
| ] |
| let result = input.map { (_) -> Void in return () } |
| expectEqual(3, result.count) |
| } |
| } |
| |
| % end |
| |
| //===----------------------------------------------------------------------===// |
| // MutableCollectionType and RangeReplaceableCollectionType conformance tests. |
| //===----------------------------------------------------------------------===// |
| |
| % for array_type in all_array_types: |
| ArrayTestSuite.test("${array_type}/AssociatedTypes") { |
| typealias Collection = ${array_type}<OpaqueValue<Int>> |
| typealias CollectionSlice = ArraySlice<OpaqueValue<Int>> |
| expectCollectionAssociatedTypes( |
| collectionType: Collection.self, |
| iteratorType: IndexingIterator<Collection>.self, |
| subSequenceType: CollectionSlice.self, |
| indexType: Int.self, |
| indexDistanceType: Int.self, |
| indicesType: CountableRange<Int>.self) |
| } |
| % end |
| |
| func ArraySliceWithNonZeroStartIndex<T>(_ elements: [T]) -> ArraySlice<T> { |
| var r = ArraySlice<T>(_startIndex: 1000) |
| r.append(contentsOf: elements) |
| expectEqual(1000, r.startIndex) |
| return r |
| } |
| |
| % for array_type in all_array_types + ['ArraySliceWithNonZeroStartIndex']: |
| % collection_or_slice = 'Slice' if 'Slice' in array_type else 'Collection' |
| |
| do { |
| // `Array`, `ArraySlice`, and `ContiguousArrayBuffer` have no expectation of |
| // failure for advancing their indexes "out of bounds", because they are just |
| // `Int`. |
| var resiliencyChecks = CollectionMisuseResiliencyChecks.all |
| resiliencyChecks.creatingOutOfBoundsIndicesBehavior = .none |
| |
| // Test MutableCollectionType conformance with value type elements. |
| ArrayTestSuite.addMutableRandomAccessCollectionTests( |
| "${array_type}.", |
| makeCollection: { (elements: [OpaqueValue<Int>]) in |
| return ${array_type}(elements) |
| }, |
| wrapValue: identity, |
| extractValue: identity, |
| makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in |
| return ${array_type}(elements) |
| }, |
| wrapValueIntoEquatable: identityEq, |
| extractValueFromEquatable: identityEq, |
| makeCollectionOfComparable: { (elements: [MinimalComparableValue]) in |
| return ${array_type}(elements) |
| }, |
| wrapValueIntoComparable: identityComp, |
| extractValueFromComparable: identityComp, |
| resiliencyChecks: resiliencyChecks, |
| withUnsafeMutableBufferPointerIsSupported: true, |
| isFixedLengthCollection: false) |
| |
| |
| // Test MutableCollectionType conformance with reference type elements. |
| ArrayTestSuite.addMutableRandomAccessCollectionTests( |
| "${array_type}.", |
| makeCollection: { (elements: [LifetimeTracked]) in |
| return ${array_type}(elements) |
| }, |
| wrapValue: { (element: OpaqueValue<Int>) in |
| LifetimeTracked(element.value, identity: element.identity) |
| }, |
| extractValue: { (element: LifetimeTracked) in |
| OpaqueValue(element.value, identity: element.identity) |
| }, |
| makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in |
| // FIXME: use LifetimeTracked. |
| return ${array_type}(elements) |
| }, |
| wrapValueIntoEquatable: identityEq, |
| extractValueFromEquatable: identityEq, |
| makeCollectionOfComparable: { (elements: [MinimalComparableValue]) in |
| // FIXME: use LifetimeTracked. |
| return ${array_type}(elements) |
| }, |
| wrapValueIntoComparable: identityComp, |
| extractValueFromComparable: identityComp, |
| resiliencyChecks: resiliencyChecks, |
| withUnsafeMutableBufferPointerIsSupported: true, |
| isFixedLengthCollection: false) |
| |
| |
| // Test RangeReplaceableCollectionType conformance with value type elements. |
| ArrayTestSuite.addRangeReplaceableRandomAccess${collection_or_slice}Tests( |
| "${array_type}.", |
| makeCollection: { (elements: [OpaqueValue<Int>]) in |
| return ${array_type}(elements) |
| }, |
| wrapValue: identity, |
| extractValue: identity, |
| makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in |
| return ${array_type}(elements) |
| }, |
| wrapValueIntoEquatable: identityEq, |
| extractValueFromEquatable: identityEq, |
| resiliencyChecks: resiliencyChecks) |
| |
| |
| // Test RangeReplaceableCollectionType conformance with reference type elements. |
| ArrayTestSuite.addRangeReplaceableRandomAccess${collection_or_slice}Tests( |
| "${array_type}.", |
| makeCollection: { (elements: [LifetimeTracked]) in |
| return ${array_type}(elements) |
| }, |
| wrapValue: { (element: OpaqueValue<Int>) in LifetimeTracked(element.value) }, |
| extractValue: { (element: LifetimeTracked) in OpaqueValue(element.value) }, |
| makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in |
| // FIXME: use LifetimeTracked. |
| return ${array_type}(elements) |
| }, |
| wrapValueIntoEquatable: identityEq, |
| extractValueFromEquatable: identityEq, |
| resiliencyChecks: resiliencyChecks) |
| } |
| |
| % end |
| |
| runAllTests() |