// 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)..() let popped = a.popLast() expectNil(popped) expectTrue(a.isEmpty) } do { var popped = [Int]() var a: ${Kind} = [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}> = ${Kind}([ 1 ].map(OpaqueValue.init)) a.removeFirst() expectEqual(0, a.startIndex) } do { var a: ${Kind}> = ${Kind}([ 1, 2 ].map(OpaqueValue.init)) a.removeFirst() expectEqual(0, a.startIndex) } } % end ArrayTestSuite.test("ArraySlice/removeFirst") { do { let a: [OpaqueValue] = [ 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] = [ 99, 1010, 2020, 99 ].map(OpaqueValue.init) var s = a[1..<2] expectEqual(1, s.startIndex) s.removeFirst() expectEqual(2, s.startIndex) } } //===----------------------------------------------------------------------===// // 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 { 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..(_ elements: [T]) -> ArraySlice { var r = ArraySlice(_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]) 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) 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]) 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) 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()