| //===----------------------------------------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| import StdlibUnittest |
| |
| internal var checksAdded: Set<String> = [] |
| |
| public struct SubscriptRangeTest { |
| public let expected: [OpaqueValue<Int>] |
| public let collection: [OpaqueValue<Int>] |
| public let bounds: Range<Int> |
| public let count: Int |
| public let loc: SourceLoc |
| |
| public var isEmpty: Bool { return count == 0 } |
| |
| public func bounds<C : Collection>(in c: C) -> Range<C.Index> { |
| let i = c.startIndex |
| return c.index(i, offsetBy: numericCast(bounds.lowerBound)) ..< |
| c.index(i, offsetBy: numericCast(bounds.upperBound)) |
| } |
| |
| public init( |
| expected: [Int], collection: [Int], bounds: Range<Int>, |
| count: Int, |
| file: String = #file, line: UInt = #line |
| ) { |
| self.expected = expected.map(OpaqueValue.init) |
| self.collection = collection.map(OpaqueValue.init) |
| self.bounds = bounds |
| self.count = count |
| self.loc = SourceLoc(file, line, comment: "test data") |
| } |
| } |
| |
| public struct PrefixThroughTest { |
| public var collection: [Int] |
| public let position: Int |
| public let expected: [Int] |
| public let loc: SourceLoc |
| |
| init( |
| collection: [Int], position: Int, expected: [Int], |
| file: String = #file, line: UInt = #line |
| ) { |
| self.collection = collection |
| self.position = position |
| self.expected = expected |
| self.loc = SourceLoc(file, line, comment: "prefix(through:) test data") |
| } |
| } |
| |
| public struct PrefixUpToTest { |
| public var collection: [Int] |
| public let end: Int |
| public let expected: [Int] |
| public let loc: SourceLoc |
| |
| public init( |
| collection: [Int], end: Int, expected: [Int], |
| file: String = #file, line: UInt = #line |
| ) { |
| self.collection = collection |
| self.end = end |
| self.expected = expected |
| self.loc = SourceLoc(file, line, comment: "prefix(upTo:) test data") |
| } |
| } |
| |
| internal struct RemoveFirstNTest { |
| let collection: [Int] |
| let numberToRemove: Int |
| let expectedCollection: [Int] |
| let loc: SourceLoc |
| |
| init( |
| collection: [Int], numberToRemove: Int, expectedCollection: [Int], |
| file: String = #file, line: UInt = #line |
| ) { |
| self.collection = collection |
| self.numberToRemove = numberToRemove |
| self.expectedCollection = expectedCollection |
| self.loc = SourceLoc(file, line, comment: "removeFirst(n: Int) test data") |
| } |
| } |
| |
| public struct SuffixFromTest { |
| public var collection: [Int] |
| public let start: Int |
| public let expected: [Int] |
| public let loc: SourceLoc |
| |
| init( |
| collection: [Int], start: Int, expected: [Int], |
| file: String = #file, line: UInt = #line |
| ) { |
| self.collection = collection |
| self.start = start |
| self.expected = expected |
| self.loc = SourceLoc(file, line, comment: "suffix(from:) test data") |
| } |
| } |
| |
| public let subscriptRangeTests = [ |
| // Slice an empty collection. |
| SubscriptRangeTest( |
| expected: [], |
| collection: [], |
| bounds: 0..<0, |
| count: 0), |
| |
| // Slice to the full extent. |
| SubscriptRangeTest( |
| expected: [1010], |
| collection: [1010], |
| bounds: 0..<1, |
| count: 1), |
| SubscriptRangeTest( |
| expected: [1010, 2020, 3030], |
| collection: [1010, 2020, 3030], |
| bounds: 0..<3, |
| count: 3), |
| SubscriptRangeTest( |
| expected: [1010, 2020, 3030, 4040, 5050], |
| collection: [1010, 2020, 3030, 4040, 5050], |
| bounds: 0..<5, |
| count: 5), |
| |
| // Slice an empty prefix. |
| SubscriptRangeTest( |
| expected: [], |
| collection: [1010, 2020, 3030], |
| bounds: 0..<0, |
| count: 3), |
| |
| // Slice a prefix. |
| SubscriptRangeTest( |
| expected: [1010, 2020], |
| collection: [1010, 2020, 3030], |
| bounds: 0..<2, |
| count: 3), |
| SubscriptRangeTest( |
| expected: [1010, 2020], |
| collection: [1010, 2020, 3030, 4040, 5050], |
| bounds: 0..<2, |
| count: 5), |
| |
| // Slice an empty suffix. |
| SubscriptRangeTest( |
| expected: [], |
| collection: [1010, 2020, 3030], |
| bounds: 3..<3, |
| count: 3), |
| |
| // Slice a suffix. |
| SubscriptRangeTest( |
| expected: [2020, 3030], |
| collection: [1010, 2020, 3030], |
| bounds: 1..<3, |
| count: 3), |
| SubscriptRangeTest( |
| expected: [4040, 5050], |
| collection: [1010, 2020, 3030, 4040, 5050], |
| bounds: 3..<5, |
| count: 5), |
| |
| // Slice an empty range in the middle. |
| SubscriptRangeTest( |
| expected: [], |
| collection: [1010, 2020, 3030], |
| bounds: 1..<1, |
| count: 3), |
| SubscriptRangeTest( |
| expected: [], |
| collection: [1010, 2020, 3030], |
| bounds: 2..<2, |
| count: 3), |
| |
| // Slice the middle part. |
| SubscriptRangeTest( |
| expected: [2020], |
| collection: [1010, 2020, 3030], |
| bounds: 1..<2, |
| count: 3), |
| SubscriptRangeTest( |
| expected: [3030], |
| collection: [1010, 2020, 3030, 4040], |
| bounds: 2..<3, |
| count: 4), |
| SubscriptRangeTest( |
| expected: [2020, 3030, 4040], |
| collection: [1010, 2020, 3030, 4040, 5050, 6060], |
| bounds: 1..<4, |
| count: 6), |
| ] |
| |
| public let prefixUpToTests = [ |
| PrefixUpToTest( |
| collection: [], |
| end: 0, |
| expected: [] |
| ), |
| PrefixUpToTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| end: 3, |
| expected: [1010, 2020, 3030] |
| ), |
| PrefixUpToTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| end: 5, |
| expected: [1010, 2020, 3030, 4040, 5050] |
| ), |
| ] |
| |
| public let prefixThroughTests = [ |
| PrefixThroughTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| position: 0, |
| expected: [1010] |
| ), |
| PrefixThroughTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| position: 2, |
| expected: [1010, 2020, 3030] |
| ), |
| PrefixThroughTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| position: 4, |
| expected: [1010, 2020, 3030, 4040, 5050] |
| ), |
| ] |
| |
| public let suffixFromTests = [ |
| SuffixFromTest( |
| collection: [], |
| start: 0, |
| expected: [] |
| ), |
| SuffixFromTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| start: 0, |
| expected: [1010, 2020, 3030, 4040, 5050] |
| ), |
| SuffixFromTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| start: 3, |
| expected: [4040, 5050] |
| ), |
| SuffixFromTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| start: 5, |
| expected: [] |
| ), |
| ] |
| |
| let removeFirstTests: [RemoveFirstNTest] = [ |
| RemoveFirstNTest( |
| collection: [1010], |
| numberToRemove: 0, |
| expectedCollection: [1010] |
| ), |
| RemoveFirstNTest( |
| collection: [1010], |
| numberToRemove: 1, |
| expectedCollection: [] |
| ), |
| RemoveFirstNTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| numberToRemove: 0, |
| expectedCollection: [1010, 2020, 3030, 4040, 5050] |
| ), |
| RemoveFirstNTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| numberToRemove: 1, |
| expectedCollection: [2020, 3030, 4040, 5050] |
| ), |
| RemoveFirstNTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| numberToRemove: 2, |
| expectedCollection: [3030, 4040, 5050] |
| ), |
| RemoveFirstNTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| numberToRemove: 3, |
| expectedCollection: [4040, 5050] |
| ), |
| RemoveFirstNTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| numberToRemove: 4, |
| expectedCollection: [5050] |
| ), |
| RemoveFirstNTest( |
| collection: [1010, 2020, 3030, 4040, 5050], |
| numberToRemove: 5, |
| expectedCollection: [] |
| ), |
| ] |
| |
| extension Collection { |
| public func nthIndex(_ offset: Int) -> Index { |
| return self.index(self.startIndex, offsetBy: numericCast(offset)) |
| } |
| } |
| |
| public struct DistanceFromToTest { |
| public let startOffset: Int |
| public let endOffset: Int |
| public var expectedDistance : Int { return endOffset - startOffset } |
| public let loc: SourceLoc |
| |
| public init( |
| start: Int, end: Int, |
| file: String = #file, line: UInt = #line |
| ) { |
| self.startOffset = start |
| self.endOffset = end |
| self.loc = SourceLoc(file, line, comment: "distance(from:to:) test data") |
| } |
| } |
| |
| public struct IndexOffsetByTest { |
| public let startOffset: Int |
| public let distance: Int |
| public let limit: Int? |
| public let expectedOffset: Int? |
| |
| public let loc: SourceLoc |
| |
| public init( |
| startOffset: Int, distance: Int, expectedOffset: Int?, |
| limitedBy limit: Int? = nil, |
| file: String = #file, line: UInt = #line |
| ) { |
| self.startOffset = startOffset |
| self.distance = distance |
| self.expectedOffset = expectedOffset |
| self.limit = limit |
| self.loc = SourceLoc(file, line, comment: "index(_:offsetBy:) test data") |
| } |
| } |
| |
| public let distanceFromToTests = [ |
| // 0 - 1: no distance. |
| DistanceFromToTest(start: 0, end: 0), |
| DistanceFromToTest(start: 10, end: 10), |
| // 2 - 3: forward distance. |
| DistanceFromToTest(start: 10, end: 13), |
| DistanceFromToTest(start: 7, end: 10), |
| // 4 - 6: backward distance. |
| DistanceFromToTest(start: 12, end: 4), |
| DistanceFromToTest(start: 8, end: 2), |
| DistanceFromToTest(start: 19, end: 0) |
| ] |
| |
| public let indexOffsetByTests = [ |
| IndexOffsetByTest(startOffset: 0, distance: 0, expectedOffset: 0), |
| IndexOffsetByTest(startOffset: 7, distance: 0, expectedOffset: 7), |
| IndexOffsetByTest(startOffset: 0, distance: -1, expectedOffset: -1), |
| IndexOffsetByTest(startOffset: 4, distance: -9, expectedOffset: -5), |
| IndexOffsetByTest(startOffset: 8, distance: -2, expectedOffset: 6), |
| IndexOffsetByTest(startOffset: 4, distance: -4, expectedOffset: 0), |
| IndexOffsetByTest(startOffset: 3, distance: 12, expectedOffset: 15), |
| IndexOffsetByTest(startOffset: 9, distance: 1, expectedOffset: 10), |
| IndexOffsetByTest(startOffset: 2, distance: 4, expectedOffset: 6), |
| IndexOffsetByTest(startOffset: 0, distance: 9, expectedOffset: 9), |
| IndexOffsetByTest( |
| startOffset: 0, distance: 0, expectedOffset: 0, limitedBy: 0), |
| IndexOffsetByTest( |
| startOffset: 0, distance: 0, expectedOffset: 0, limitedBy: 10), |
| IndexOffsetByTest( |
| startOffset: 0, distance: 10, expectedOffset: nil, limitedBy: 0), |
| IndexOffsetByTest( |
| startOffset: 0, distance: -10, expectedOffset: nil, limitedBy: 0), |
| IndexOffsetByTest( |
| startOffset: 0, distance: 10, expectedOffset: 10, limitedBy: 10), |
| IndexOffsetByTest( |
| startOffset: 0, distance: 20, expectedOffset: nil, limitedBy: 10), |
| IndexOffsetByTest( |
| startOffset: 10, distance: -20, expectedOffset: nil, limitedBy: 0), |
| IndexOffsetByTest( |
| startOffset: 5, distance: 5, expectedOffset: 10, limitedBy: 2), |
| IndexOffsetByTest( |
| startOffset: 5, distance: 5, expectedOffset: nil, limitedBy: 5), |
| IndexOffsetByTest( |
| startOffset: 5, distance: -5, expectedOffset: 0, limitedBy: 7), |
| IndexOffsetByTest( |
| startOffset: 5, distance: -5, expectedOffset: nil, limitedBy: 5) |
| ] |
| |
| public struct IndexAfterTest { |
| public let start: Int |
| public let end: Int |
| public let loc: SourceLoc |
| |
| public init(start: Int, end: Int, file: String = #file, line: UInt = #line) { |
| self.start = start |
| self.end = end |
| self.loc = SourceLoc(file, line, |
| comment: "index(after:) and formIndex(after:) test data") |
| } |
| } |
| |
| public let indexAfterTests = [ |
| IndexAfterTest(start: 0, end: 1), |
| IndexAfterTest(start: 0, end: 10), |
| IndexAfterTest(start: 5, end: 12), |
| IndexAfterTest(start: -58, end: -54), |
| ] |
| |
| internal func _allIndices<C : Collection>( |
| into c: C, in bounds: Range<C.Index> |
| ) -> [C.Index] { |
| var result: [C.Index] = [] |
| var i = bounds.lowerBound |
| while i != bounds.upperBound { |
| result.append(i) |
| i = c.index(after: i) |
| } |
| return result |
| } |
| |
| internal enum _SubSequenceSubscriptOnIndexMode { |
| case inRange |
| case outOfRangeToTheLeft |
| case outOfRangeToTheRight |
| case baseEndIndex |
| case sliceEndIndex |
| |
| static var all: [_SubSequenceSubscriptOnIndexMode] { |
| return [ |
| .inRange, |
| .outOfRangeToTheLeft, |
| .outOfRangeToTheRight, |
| .baseEndIndex, |
| .sliceEndIndex, |
| ] |
| } |
| } |
| |
| internal enum _SubSequenceSubscriptOnRangeMode { |
| case inRange |
| case outOfRangeToTheLeftEmpty |
| case outOfRangeToTheLeftNonEmpty |
| case outOfRangeToTheRightEmpty |
| case outOfRangeToTheRightNonEmpty |
| case outOfRangeBothSides |
| case baseEndIndex |
| |
| static var all: [_SubSequenceSubscriptOnRangeMode] { |
| return [ |
| .inRange, |
| .outOfRangeToTheLeftEmpty, |
| .outOfRangeToTheLeftNonEmpty, |
| .outOfRangeToTheRightEmpty, |
| .outOfRangeToTheRightNonEmpty, |
| .outOfRangeBothSides, |
| .baseEndIndex, |
| ] |
| } |
| } |
| |
| %{ |
| from gyb_stdlib_support import collectionForTraversal |
| def testConstraints(protocol): |
| if protocol == 'Collection': |
| subseq_as_collection = 'CollectionWithEquatableElement.SubSequence : Collection,' |
| else: |
| subseq_as_collection='' |
| return ''' |
| C : %(protocol)s, |
| CollectionWithEquatableElement : %(protocol)s, |
| %(subseq_as_collection)s |
| C.SubSequence : %(protocol)s, |
| C.Indices : %(protocol)s, |
| CollectionWithEquatableElement.Iterator.Element : Equatable |
| ''' % locals() |
| |
| testParams = ''' |
| _ testNamePrefix: String = "", |
| makeCollection: @escaping ([C.Iterator.Element]) -> C, |
| wrapValue: @escaping (OpaqueValue<Int>) -> C.Iterator.Element, |
| extractValue: @escaping (C.Iterator.Element) -> OpaqueValue<Int>, |
| |
| makeCollectionOfEquatable: @escaping ( |
| [CollectionWithEquatableElement.Iterator.Element] |
| ) -> CollectionWithEquatableElement, |
| |
| wrapValueIntoEquatable: @escaping ( |
| MinimalEquatableValue) -> CollectionWithEquatableElement.Iterator.Element, |
| |
| extractValueFromEquatable: @escaping ((CollectionWithEquatableElement.Iterator.Element) -> MinimalEquatableValue), |
| |
| resiliencyChecks: CollectionMisuseResiliencyChecks = .all, |
| outOfBoundsIndexOffset: Int = 1, |
| outOfBoundsSubscriptOffset: Int = 1, |
| collectionIsBidirectional: Bool |
| ''' |
| |
| import re |
| forwardTestArgs = ',\n '.join( |
| [ x.group(1) + ': ' + x.group(1) |
| for x in re.finditer(r'([a-zA-Z0-9_]+):', testParams) ] |
| ).replace('testNamePrefix: ', '\n ') |
| }% |
| extension TestSuite { |
| public func addCollectionTests< |
| C, CollectionWithEquatableElement |
| >( |
| ${testParams} = false |
| ) where |
| ${testConstraints('Collection')} { |
| |
| var testNamePrefix = testNamePrefix |
| |
| if !checksAdded.insert( |
| "\(testNamePrefix).\(C.self).\(#function)" |
| ).inserted { |
| return |
| } |
| |
| addSequenceTests( |
| testNamePrefix, |
| makeSequence: makeCollection, |
| wrapValue: wrapValue, |
| extractValue: extractValue, |
| makeSequenceOfEquatable: makeCollectionOfEquatable, |
| wrapValueIntoEquatable: wrapValueIntoEquatable, |
| extractValueFromEquatable: extractValueFromEquatable, |
| resiliencyChecks: resiliencyChecks) |
| |
| func makeWrappedCollection(_ elements: [OpaqueValue<Int>]) -> C { |
| return makeCollection(elements.map(wrapValue)) |
| } |
| |
| func makeWrappedCollectionWithEquatableElement( |
| _ elements: [MinimalEquatableValue] |
| ) -> CollectionWithEquatableElement { |
| return makeCollectionOfEquatable(elements.map(wrapValueIntoEquatable)) |
| } |
| |
| testNamePrefix += String(describing: C.Type.self) |
| |
| //===------------------------------------------------------------------===// |
| // generate() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).generate()/semantics") { |
| for test in subscriptRangeTests { |
| let c = makeWrappedCollection(test.collection) |
| for _ in 0..<3 { |
| checkSequence( |
| test.collection.map(wrapValue), |
| c, |
| resiliencyChecks: .none) { |
| extractValue($0).value == extractValue($1).value |
| } |
| } |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // Index |
| //===------------------------------------------------------------------===// |
| |
| if resiliencyChecks.creatingOutOfBoundsIndicesBehavior != .none { |
| self.test("\(testNamePrefix).Index/OutOfBounds/Right/NonEmpty") { |
| let c = makeWrappedCollection([ 1010, 2020, 3030 ].map(OpaqueValue.init)) |
| let index = c.endIndex |
| expectCrashLater() |
| _blackHole(c.index(index, offsetBy: numericCast(outOfBoundsIndexOffset))) |
| } |
| |
| self.test("\(testNamePrefix).Index/OutOfBounds/Right/Empty") { |
| let c = makeWrappedCollection([]) |
| let index = c.endIndex |
| expectCrashLater() |
| _blackHole(c.index(index, offsetBy: numericCast(outOfBoundsIndexOffset))) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // subscript(_: Index) |
| //===------------------------------------------------------------------===// |
| |
| if resiliencyChecks.subscriptOnOutOfBoundsIndicesBehavior != .none { |
| self.test("\(testNamePrefix).subscript(_: Index)/OutOfBounds/Right/NonEmpty/Get") { |
| let c = makeWrappedCollection([ 1010, 2020, 3030 ].map(OpaqueValue.init)) |
| var index = c.endIndex |
| expectCrashLater() |
| index = c.index(index, offsetBy: numericCast(outOfBoundsSubscriptOffset)) |
| _blackHole(c[index]) |
| } |
| |
| self.test("\(testNamePrefix).subscript(_: Index)/OutOfBounds/Right/Empty/Get") { |
| let c = makeWrappedCollection([]) |
| var index = c.endIndex |
| expectCrashLater() |
| index = c.index(index, offsetBy: numericCast(outOfBoundsSubscriptOffset)) |
| _blackHole(c[index]) |
| } |
| |
| let tests = cartesianProduct( |
| subscriptRangeTests, |
| _SubSequenceSubscriptOnIndexMode.all) |
| |
| self.test("\(testNamePrefix).SubSequence.subscript(_: Index)/Get/OutOfBounds") |
| .forEach(in: tests) { |
| (test, mode) in |
| let elements = test.collection |
| let sliceFromLeft = test.bounds.lowerBound |
| let sliceFromRight = elements.count - test.bounds.upperBound |
| print("\(elements)/sliceFromLeft=\(sliceFromLeft)/sliceFromRight=\(sliceFromRight)") |
| let base = makeWrappedCollection(elements) |
| let sliceStartIndex = |
| base.index(base.startIndex, offsetBy: numericCast(sliceFromLeft)) |
| let sliceEndIndex = base.index( |
| base.startIndex, |
| offsetBy: numericCast(elements.count - sliceFromRight)) |
| var slice = base[sliceStartIndex..<sliceEndIndex] |
| expectType(C.SubSequence.self, &slice) |
| |
| var index: C.Index = base.startIndex |
| switch mode { |
| case .inRange: |
| let sliceNumericIndices = |
| sliceFromLeft..<(elements.count - sliceFromRight) |
| for (i, index) in base.indices.enumerated() { |
| if sliceNumericIndices.contains(i) { |
| expectEqual( |
| elements[i].value, |
| extractValue(slice[index]).value) |
| expectEqual( |
| extractValue(base[index]).value, |
| extractValue(slice[index]).value) |
| } |
| } |
| return |
| case .outOfRangeToTheLeft: |
| if sliceFromLeft == 0 { return } |
| index = base.index( |
| base.startIndex, |
| offsetBy: numericCast(sliceFromLeft - 1)) |
| case .outOfRangeToTheRight: |
| if sliceFromRight == 0 { return } |
| index = base.index( |
| base.startIndex, |
| offsetBy: numericCast(elements.count - sliceFromRight)) |
| case .baseEndIndex: |
| index = base.endIndex |
| case .sliceEndIndex: |
| index = sliceEndIndex |
| } |
| |
| expectCrashLater() |
| _blackHole(slice[index]) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // subscript(_: Range) |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).subscript(_: Range)/Get/semantics") { |
| for test in subscriptRangeTests { |
| let base = makeWrappedCollection(test.collection) |
| let sliceBounds = test.bounds(in: base) |
| let slice = base[sliceBounds] |
| |
| expectEqual(sliceBounds.lowerBound, slice.startIndex) |
| expectEqual(sliceBounds.upperBound, slice.endIndex) |
| /* |
| // TODO: swift-3-indexing-model: uncomment the following. |
| // FIXME: improve checkForwardCollection to check the SubSequence type. |
| checkCollection( |
| expected: test.expected.map(wrapValue), |
| slice, |
| resiliencyChecks: .none) { |
| extractValue($0).value == extractValue($1).value |
| } |
| */ |
| } |
| } |
| |
| if resiliencyChecks.subscriptRangeOnOutOfBoundsRangesBehavior != .none { |
| self.test("\(testNamePrefix).subscript(_: Range)/OutOfBounds/Right/NonEmpty/Get") { |
| let c = makeWrappedCollection([ 1010, 2020, 3030 ].map(OpaqueValue.init)) |
| var index = c.endIndex |
| expectCrashLater() |
| index = c.index(index, offsetBy: numericCast(outOfBoundsSubscriptOffset)) |
| _blackHole(c[index..<index]) |
| } |
| |
| self.test("\(testNamePrefix).subscript(_: Range)/OutOfBounds/Right/Empty/Get") { |
| let c = makeWrappedCollection([]) |
| var index = c.endIndex |
| expectCrashLater() |
| index = c.index(index, offsetBy: numericCast(outOfBoundsSubscriptOffset)) |
| _blackHole(c[index..<index]) |
| } |
| |
| let tests = cartesianProduct( |
| subscriptRangeTests, |
| _SubSequenceSubscriptOnRangeMode.all) |
| |
| self.test( |
| "\(testNamePrefix).SubSequence.subscript(_: Range)/Get/OutOfBounds") |
| .forEach(in: tests) { |
| (test, mode) in |
| let elements = test.collection |
| let sliceFromLeft = test.bounds.lowerBound |
| let sliceFromRight = elements.count - test.bounds.upperBound |
| print( |
| "\(elements)/sliceFromLeft=\(sliceFromLeft)" |
| + "/sliceFromRight=\(sliceFromRight)") |
| let base = makeWrappedCollection(elements) |
| let sliceStartIndex = |
| base.index(base.startIndex, offsetBy: numericCast(sliceFromLeft)) |
| let sliceEndIndex = base.index( |
| base.startIndex, |
| offsetBy: numericCast(elements.count - sliceFromRight)) |
| var slice = base[sliceStartIndex..<sliceEndIndex] |
| expectType(C.SubSequence.self, &slice) |
| |
| var bounds: Range<C.Index> = base.startIndex..<base.startIndex |
| switch mode { |
| case .inRange: |
| let sliceNumericIndices = |
| sliceFromLeft..<(elements.count - sliceFromRight + 1) |
| for (i, subSliceStartIndex) in base.indices.enumerated() { |
| for (j, subSliceEndIndex) in base.indices.enumerated() { |
| if i <= j && |
| sliceNumericIndices.contains(i) && |
| sliceNumericIndices.contains(j) { |
| let subSlice = slice[subSliceStartIndex..<subSliceEndIndex] |
| for (k, index) in subSlice.indices.enumerated() { |
| expectEqual( |
| elements[i + k].value, |
| extractValue(subSlice[index]).value) |
| expectEqual( |
| extractValue(base[index]).value, |
| extractValue(subSlice[index]).value) |
| expectEqual( |
| extractValue(slice[index]).value, |
| extractValue(subSlice[index]).value) |
| } |
| } |
| } |
| } |
| return |
| case .outOfRangeToTheLeftEmpty: |
| if sliceFromLeft == 0 { return } |
| let index = base.index( |
| base.startIndex, |
| offsetBy: numericCast(sliceFromLeft - 1)) |
| bounds = index..<index |
| break |
| case .outOfRangeToTheLeftNonEmpty: |
| if sliceFromLeft == 0 { return } |
| let index = base.index( |
| base.startIndex, |
| offsetBy: numericCast(sliceFromLeft - 1)) |
| bounds = index..<sliceStartIndex |
| break |
| case .outOfRangeToTheRightEmpty: |
| if sliceFromRight == 0 { return } |
| let index = base.index( |
| base.startIndex, |
| offsetBy: numericCast(elements.count - sliceFromRight + 1)) |
| bounds = index..<index |
| break |
| case .outOfRangeToTheRightNonEmpty: |
| if sliceFromRight == 0 { return } |
| let index = base.index( |
| base.startIndex, |
| offsetBy: numericCast(elements.count - sliceFromRight + 1)) |
| bounds = sliceEndIndex..<index |
| break |
| case .outOfRangeBothSides: |
| if sliceFromLeft == 0 { return } |
| if sliceFromRight == 0 { return } |
| bounds = |
| base.index( |
| base.startIndex, |
| offsetBy: numericCast(sliceFromLeft - 1)) |
| ..< |
| base.index( |
| base.startIndex, |
| offsetBy: numericCast(elements.count - sliceFromRight + 1)) |
| break |
| case .baseEndIndex: |
| if sliceFromRight == 0 { return } |
| bounds = sliceEndIndex..<base.endIndex |
| break |
| } |
| |
| expectCrashLater() |
| _blackHole(slice[bounds]) |
| } |
| } |
| |
| // FIXME: swift-3-indexing-model - add tests for the following? |
| // _failEarlyRangeCheck(index: Index, bounds: Range<Index>) |
| // _failEarlyRangeCheck(range: Range<Index>, bounds: Range<Index>) |
| |
| //===------------------------------------------------------------------===// |
| // isEmpty |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).isEmpty/semantics") { |
| for test in subscriptRangeTests { |
| let c = makeWrappedCollection(test.collection) |
| expectEqual(test.isEmpty, c.isEmpty) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // count |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).count/semantics") { |
| for test in subscriptRangeTests { |
| let c = makeWrappedCollection(test.collection) |
| expectEqual(test.count, numericCast(c.count) as Int) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // index(of:)/index(where:) |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).index(of:)/semantics") { |
| for test in findTests { |
| let c = makeWrappedCollectionWithEquatableElement(test.sequence) |
| var result = c.index(of: wrapValueIntoEquatable(test.element)) |
| expectType( |
| Optional<CollectionWithEquatableElement.Index>.self, |
| &result) |
| let zeroBasedIndex = result.map { |
| numericCast(c.distance(from: c.startIndex, to: $0)) as Int |
| } |
| expectEqual( |
| test.expected, |
| zeroBasedIndex, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| self.test("\(testNamePrefix).index(where:)/semantics") { |
| for test in findTests { |
| let closureLifetimeTracker = LifetimeTracked(0) |
| expectEqual(1, LifetimeTracked.instances) |
| let c = makeWrappedCollectionWithEquatableElement(test.sequence) |
| let result = c.index { |
| (candidate) in |
| _blackHole(closureLifetimeTracker) |
| return |
| extractValueFromEquatable(candidate).value == test.element.value |
| } |
| let zeroBasedIndex = result.map { |
| numericCast(c.distance(from: c.startIndex, to: $0)) as Int |
| } |
| expectEqual( |
| test.expected, |
| zeroBasedIndex, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // first |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).first") { |
| for test in subscriptRangeTests { |
| let c = makeWrappedCollection(test.collection) |
| let result = c.first |
| if test.isEmpty { |
| expectNil(result) |
| } else { |
| expectOptionalEqual( |
| test.collection[0], |
| result.map(extractValue) |
| ) { $0.value == $1.value } |
| } |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // indices |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).indices") { |
| // TODO: swift-3-indexing-model: improve this test. `indices` |
| // is not just a `Range` anymore, it can be anything. |
| for test in subscriptRangeTests { |
| let c = makeWrappedCollection(test.collection) |
| let indices = c.indices |
| expectEqual(c.startIndex, indices.startIndex) |
| expectEqual(c.endIndex, indices.endIndex) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // dropFirst() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).dropFirst/semantics") { |
| for test in dropFirstTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.dropFirst(test.dropElements) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // dropLast() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).dropLast/semantics") { |
| for test in dropLastTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.dropLast(test.dropElements) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // prefix() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).prefix/semantics") { |
| for test in prefixTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.prefix(test.maxLength) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // suffix() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).suffix/semantics") { |
| for test in suffixTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.suffix(test.maxLength) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // split() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).split/semantics") { |
| for test in splitTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.split( |
| maxSplits: test.maxSplits, |
| omittingEmptySubsequences: test.omittingEmptySubsequences |
| ) { |
| extractValue($0).value == test.separator |
| } |
| expectEqualSequence(test.expected, result.map { |
| $0.map { |
| extractValue($0).value |
| } |
| }, |
| stackTrace: SourceLocStack().with(test.loc)) { $0 == $1 } |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // prefix(through:) |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).prefix(through:)/semantics") { |
| for test in prefixThroughTests { |
| let c = makeWrappedCollection(test.collection.map(OpaqueValue.init)) |
| let index = c.index( |
| c.startIndex, offsetBy: numericCast(test.position)) |
| let result = c.prefix(through: index) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // prefix(upTo:) |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).prefix(upTo:)/semantics") { |
| for test in prefixUpToTests { |
| let c = makeWrappedCollection(test.collection.map(OpaqueValue.init)) |
| let index = c.index(c.startIndex, offsetBy: numericCast(test.end)) |
| let result = c.prefix(upTo: index) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // suffix(from:) |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).suffix(from:)/semantics") { |
| for test in suffixFromTests { |
| let c = makeWrappedCollection(test.collection.map(OpaqueValue.init)) |
| let index = c.index(c.startIndex, offsetBy: numericCast(test.start)) |
| let result = c.suffix(from: index) |
| |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // removeFirst()/slice |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).removeFirst()/slice/semantics") { |
| for test in removeFirstTests.filter({ $0.numberToRemove == 1 }) { |
| let c = makeWrappedCollection(test.collection.map(OpaqueValue.init)) |
| var slice = c[...] |
| let survivingIndices = _allIndices( |
| into: slice, |
| in: slice.index(after: slice.startIndex)..<slice.endIndex) |
| let removedElement = slice.removeFirst() |
| expectEqual(test.collection.first, extractValue(removedElement).value) |
| expectEqualSequence( |
| test.expectedCollection, |
| slice.map { extractValue($0).value }, |
| "removeFirst() shouldn't mutate the tail of the slice", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.expectedCollection, |
| survivingIndices.map { extractValue(slice[$0]).value }, |
| "removeFirst() shouldn't invalidate indices", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.collection, |
| c.map { extractValue($0).value }, |
| "removeFirst() shouldn't mutate the collection that was sliced", |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| self.test("\(testNamePrefix).removeFirst()/slice/empty/semantics") { |
| let c = makeWrappedCollection(Array<OpaqueValue<Int>>()) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| _ = slice.removeFirst() // Should trap. |
| } |
| |
| //===------------------------------------------------------------------===// |
| // removeFirst(n: Int)/slice |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).removeFirst(n: Int)/slice/semantics") { |
| for test in removeFirstTests { |
| let c = makeWrappedCollection(test.collection.map(OpaqueValue.init)) |
| var slice = c[...] |
| let survivingIndices = _allIndices( |
| into: slice, |
| in: slice.index( |
| slice.startIndex, |
| offsetBy: numericCast(test.numberToRemove)) ..< slice.endIndex |
| ) |
| slice.removeFirst(test.numberToRemove) |
| expectEqualSequence( |
| test.expectedCollection, |
| slice.map { extractValue($0).value }, |
| "removeFirst() shouldn't mutate the tail of the slice", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.expectedCollection, |
| survivingIndices.map { extractValue(slice[$0]).value }, |
| "removeFirst() shouldn't invalidate indices", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.collection, |
| c.map { extractValue($0).value }, |
| "removeFirst() shouldn't mutate the collection that was sliced", |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| self.test("\(testNamePrefix).removeFirst(n: Int)/slice/empty/semantics") { |
| let c = makeWrappedCollection(Array<OpaqueValue<Int>>()) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| slice.removeFirst(1) // Should trap. |
| } |
| |
| self.test( |
| "\(testNamePrefix).removeFirst(n: Int)/slice/removeNegative/semantics") { |
| let c = makeWrappedCollection([1010, 2020].map(OpaqueValue.init)) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| slice.removeFirst(-1) // Should trap. |
| } |
| |
| self.test( |
| "\(testNamePrefix).removeFirst(n: Int)/slice/removeTooMany/semantics") { |
| let c = makeWrappedCollection([1010, 2020].map(OpaqueValue.init)) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| slice.removeFirst(3) // Should trap. |
| } |
| |
| //===------------------------------------------------------------------===// |
| // popFirst()/slice |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).popFirst()/slice/semantics") { |
| // This can just reuse the test data for removeFirst() |
| for test in removeFirstTests.filter({ $0.numberToRemove == 1 }) { |
| let c = makeWrappedCollection(test.collection.map(OpaqueValue.init)) |
| var slice = c[...] |
| let survivingIndices = _allIndices( |
| into: slice, |
| in: slice.index(after: slice.startIndex)..<slice.endIndex) |
| let removedElement = slice.popFirst()! |
| expectEqual(test.collection.first, extractValue(removedElement).value) |
| expectEqualSequence( |
| test.expectedCollection, |
| slice.map { extractValue($0).value }, |
| "popFirst() shouldn't mutate the tail of the slice", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.expectedCollection, |
| survivingIndices.map { extractValue(slice[$0]).value }, |
| "popFirst() shouldn't invalidate indices", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.collection, |
| c.map { extractValue($0).value }, |
| "popFirst() shouldn't mutate the collection that was sliced", |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| self.test("\(testNamePrefix).popFirst()/slice/empty/semantics") { |
| let c = makeWrappedCollection(Array<OpaqueValue<Int>>()) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectNil(slice.popFirst()) |
| } |
| |
| //===------------------------------------------------------------------===// |
| self.addCommonTests(${forwardTestArgs}) |
| } // addCollectionTests |
| |
| public func addBidirectionalCollectionTests< |
| C, CollectionWithEquatableElement |
| >( |
| ${testParams} = true |
| ) where |
| ${testConstraints('BidirectionalCollection')} { |
| |
| var testNamePrefix = testNamePrefix |
| |
| if !checksAdded.insert( |
| "\(testNamePrefix).\(C.self).\(#function)" |
| ).inserted { |
| return |
| } |
| |
| addCollectionTests(${forwardTestArgs}) |
| |
| func makeWrappedCollection(_ elements: [OpaqueValue<Int>]) -> C { |
| return makeCollection(elements.map(wrapValue)) |
| } |
| |
| testNamePrefix += String(describing: C.Type.self) |
| |
| // FIXME: swift-3-indexing-model - add tests for the follow? |
| // index(before: of i: Index) -> Index |
| // formIndex(before: i: inout Index) |
| |
| // FIXME: swift-3-indexing-model - |
| // enhance the following for negative direction? |
| // advance(i: Index, by n: IndexDistance) -> Index |
| // advance( |
| // i: Index, by n: IndexDistance, limitedBy: Index) -> Index |
| // distance(from start: Index, to end: Index) -> IndexDistance |
| |
| //===------------------------------------------------------------------===// |
| // last |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).last") { |
| for test in subscriptRangeTests { |
| let c = makeWrappedCollection(test.collection) |
| let result = c.last |
| if test.isEmpty { |
| expectNil(result) |
| } else { |
| expectOptionalEqual( |
| test.collection[test.count - 1], |
| result.map(extractValue) |
| ) { $0.value == $1.value } |
| } |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // removeLast()/slice |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).removeLast()/slice/semantics") { |
| for test in removeLastTests.filter({ $0.numberToRemove == 1 }) { |
| let c = makeWrappedCollection(test.collection) |
| var slice = c[...] |
| let survivingIndices = _allIndices( |
| into: slice, |
| in: slice.startIndex |
| ..< slice.index( |
| slice.endIndex, offsetBy: numericCast(-test.numberToRemove)) |
| ) |
| let removedElement = slice.removeLast() |
| expectEqual( |
| test.collection.last!.value, |
| extractValue(removedElement).value) |
| expectEqualSequence( |
| test.expectedCollection, |
| slice.map { extractValue($0).value }, |
| "removeLast() shouldn't mutate the head of the slice", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.expectedCollection, |
| survivingIndices.map { extractValue(slice[$0]).value }, |
| "removeLast() shouldn't invalidate indices", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.collection.map { $0.value }, |
| c.map { extractValue($0).value }, |
| "removeLast() shouldn't mutate the collection that was sliced", |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| self.test("\(testNamePrefix).removeLast()/slice/empty/semantics") { |
| let c = makeWrappedCollection(Array<OpaqueValue<Int>>()) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| _ = slice.removeLast() // Should trap. |
| } |
| |
| //===------------------------------------------------------------------===// |
| // removeLast(n: Int)/slice |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).removeLast(n: Int)/slice/semantics") { |
| for test in removeLastTests { |
| let c = makeWrappedCollection(test.collection) |
| var slice = c[...] |
| let survivingIndices = _allIndices( |
| into: slice, |
| in: slice.startIndex |
| ..< slice.index( |
| slice.endIndex, offsetBy: numericCast(-test.numberToRemove)) |
| ) |
| slice.removeLast(test.numberToRemove) |
| expectEqualSequence( |
| test.expectedCollection, |
| slice.map { extractValue($0).value }, |
| "removeLast() shouldn't mutate the head of the slice", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.expectedCollection, |
| survivingIndices.map { extractValue(slice[$0]).value }, |
| "removeLast() shouldn't invalidate indices", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.collection.map { $0.value }, |
| c.map { extractValue($0).value }, |
| "removeLast() shouldn't mutate the collection that was sliced", |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| self.test("\(testNamePrefix).removeLast(n: Int)/slice/empty/semantics") { |
| let c = makeWrappedCollection(Array<OpaqueValue<Int>>()) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| slice.removeLast(1) // Should trap. |
| } |
| |
| self.test( |
| "\(testNamePrefix).removeLast(n: Int)/slice/removeNegative/semantics" |
| ) { |
| let c = makeWrappedCollection([1010, 2020].map(OpaqueValue.init)) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| slice.removeLast(-1) // Should trap. |
| } |
| |
| self.test( |
| "\(testNamePrefix).removeLast(n: Int)/slice/removeTooMany/semantics" |
| ) { |
| let c = makeWrappedCollection([1010, 2020].map(OpaqueValue.init)) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectCrashLater() |
| slice.removeLast(3) // Should trap. |
| } |
| |
| //===------------------------------------------------------------------===// |
| // popLast()/slice |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).popLast()/slice/semantics") { |
| // This can just reuse the test data for removeLast() |
| for test in removeLastTests.filter({ $0.numberToRemove == 1 }) { |
| let c = makeWrappedCollection(test.collection) |
| var slice = c[...] |
| let survivingIndices = _allIndices( |
| into: slice, |
| in: slice.startIndex |
| ..< slice.index( |
| slice.endIndex, offsetBy: numericCast(-test.numberToRemove)) |
| ) |
| let removedElement = slice.popLast()! |
| expectEqual( |
| test.collection.last!.value, |
| extractValue(removedElement).value) |
| expectEqualSequence( |
| test.expectedCollection, |
| slice.map { extractValue($0).value }, |
| "popLast() shouldn't mutate the head of the slice", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.expectedCollection, |
| survivingIndices.map { extractValue(slice[$0]).value }, |
| "popLast() shouldn't invalidate indices", |
| stackTrace: SourceLocStack().with(test.loc) |
| ) |
| expectEqualSequence( |
| test.collection.map { $0.value }, |
| c.map { extractValue($0).value }, |
| "popLast() shouldn't mutate the collection that was sliced", |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| self.test("\(testNamePrefix).popLast()/slice/empty/semantics") { |
| let c = makeWrappedCollection(Array<OpaqueValue<Int>>()) |
| var slice = c[c.startIndex..<c.startIndex] |
| expectNil(slice.popLast()) |
| } |
| |
| |
| //===------------------------------------------------------------------===// |
| // Index |
| //===------------------------------------------------------------------===// |
| |
| if resiliencyChecks.creatingOutOfBoundsIndicesBehavior != .none { |
| self.test("\(testNamePrefix).Index/OutOfBounds/Left/NonEmpty") { |
| let c = makeWrappedCollection( |
| [ 1010, 2020, 3030 ].map(OpaqueValue.init)) |
| let index = c.startIndex |
| expectCrashLater() |
| _blackHole( |
| c.index(index, offsetBy: numericCast(-outOfBoundsIndexOffset))) |
| } |
| |
| self.test("\(testNamePrefix).Index/OutOfBounds/Left/Empty") { |
| let c = makeWrappedCollection([]) |
| let index = c.startIndex |
| expectCrashLater() |
| _blackHole( |
| c.index(index, offsetBy: numericCast(-outOfBoundsIndexOffset))) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // subscript(_: Index) |
| //===------------------------------------------------------------------===// |
| |
| if resiliencyChecks.subscriptOnOutOfBoundsIndicesBehavior != .none { |
| self.test( |
| "\(testNamePrefix).subscript(_: Index)/OutOfBounds/Left/NonEmpty/Get" |
| ) { |
| let c = makeWrappedCollection( |
| [ 1010, 2020, 3030 ].map(OpaqueValue.init)) |
| var index = c.startIndex |
| expectCrashLater() |
| index = c.index( |
| index, offsetBy: numericCast(-outOfBoundsSubscriptOffset)) |
| _blackHole(c[index]) |
| } |
| |
| self.test( |
| "\(testNamePrefix).subscript(_: Index)/OutOfBounds/Left/Empty/Get" |
| ) { |
| let c = makeWrappedCollection([]) |
| var index = c.startIndex |
| expectCrashLater() |
| index = c.index( |
| index, offsetBy: numericCast(-outOfBoundsSubscriptOffset)) |
| _blackHole(c[index]) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // subscript(_: Range) |
| //===------------------------------------------------------------------===// |
| |
| if resiliencyChecks.subscriptRangeOnOutOfBoundsRangesBehavior != .none { |
| self.test( |
| "\(testNamePrefix).subscript(_: Range)/OutOfBounds/Left/NonEmpty/Get" |
| ) { |
| let c = makeWrappedCollection( |
| [ 1010, 2020, 3030 ].map(OpaqueValue.init)) |
| var index = c.startIndex |
| expectCrashLater() |
| index = c.index( |
| index, offsetBy: numericCast(-outOfBoundsSubscriptOffset)) |
| _blackHole(c[index..<index]) |
| } |
| |
| self.test( |
| "\(testNamePrefix).subscript(_: Range)/OutOfBounds/Left/Empty/Get" |
| ) { |
| let c = makeWrappedCollection([]) |
| var index = c.startIndex |
| expectCrashLater() |
| index = c.index( |
| index, offsetBy: numericCast(-outOfBoundsSubscriptOffset)) |
| _blackHole(c[index..<index]) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // dropLast() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).dropLast/semantics") { |
| for test in dropLastTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.dropLast(test.dropElements) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // suffix() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).suffix/semantics") { |
| for test in suffixTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.suffix(test.maxLength) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| |
| self.addCommonTests(${forwardTestArgs}) |
| } // addBidirectionalCollectionTests |
| |
| public func addRandomAccessCollectionTests< |
| C, CollectionWithEquatableElement |
| >( |
| ${testParams} = true |
| ) where |
| ${testConstraints('RandomAccessCollection')} { |
| |
| var testNamePrefix = testNamePrefix |
| |
| if !checksAdded.insert( |
| "\(testNamePrefix).\(C.self).\(#function)" |
| ).inserted { |
| return |
| } |
| |
| addBidirectionalCollectionTests(${forwardTestArgs}) |
| |
| testNamePrefix += String(describing: C.Type.self) |
| |
| func makeWrappedCollection(_ elements: [OpaqueValue<Int>]) -> C { |
| return makeCollection(elements.map(wrapValue)) |
| } |
| |
| //===------------------------------------------------------------------===// |
| // prefix() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).prefix/semantics") { |
| for test in prefixTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.prefix(test.maxLength) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| // suffix() |
| //===------------------------------------------------------------------===// |
| |
| self.test("\(testNamePrefix).suffix/semantics") { |
| for test in suffixTests { |
| let s = makeWrappedCollection(test.sequence.map(OpaqueValue.init)) |
| let result = s.suffix(test.maxLength) |
| expectEqualSequence( |
| test.expected, result.map(extractValue).map { $0.value }, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| |
| //===------------------------------------------------------------------===// |
| self.addCommonTests(${forwardTestArgs}) |
| } // addRandomAccessCollectionTests |
| |
| % for Traversal in ['Forward', 'Bidirectional', 'RandomAccess']: |
| func addCommonTests< |
| C, CollectionWithEquatableElement |
| >( |
| ${testParams} |
| ) where |
| ${testConstraints(collectionForTraversal(Traversal))} { |
| |
| if !checksAdded.insert( |
| "\(testNamePrefix).\(C.self).\(#function)" |
| ).inserted { |
| return |
| } |
| |
| func toCollection(_ r: CountableRange<Int>) -> C { |
| return makeCollection(r.map { wrapValue(OpaqueValue($0)) }) |
| } |
| |
| % for function_name in ['index', 'formIndex']: |
| self.test("\(testNamePrefix).${function_name}(after:)/semantics") { |
| for test in indexAfterTests { |
| let c = toCollection(test.start..<test.end) |
| var currentIndex = c.startIndex |
| var counter = test.start |
| repeat { |
| expectEqual(counter, extractValue(c[currentIndex]).value, |
| stackTrace: SourceLocStack().with(test.loc)) |
| % if function_name is 'index': |
| currentIndex = c.index(after: currentIndex) |
| % else: |
| c.formIndex(after: ¤tIndex) |
| % end |
| counter += 1 |
| } while counter < test.end |
| } |
| } |
| % end |
| |
| self.test("\(testNamePrefix)/distance(from:to:)/semantics") |
| .forEach(in: distanceFromToTests) { |
| test in |
| let c = toCollection(0..<20) |
| let backwards = (test.startOffset > test.endOffset) |
| if backwards && !collectionIsBidirectional { |
| expectCrashLater() |
| } |
| |
| let d = c.distance( |
| from: c.nthIndex(test.startOffset), to: c.nthIndex(test.endOffset)) |
| expectEqual( |
| numericCast(test.expectedDistance), |
| d, stackTrace: SourceLocStack().with(test.loc)) |
| } |
| |
| self.test("\(testNamePrefix)/index(_:offsetBy: n)/semantics") |
| .forEach( |
| in: indexOffsetByTests.filter { $0.limit == nil && $0.distance >= 0 } |
| ) { |
| test in |
| let max = 10 |
| let c = toCollection(0..<max) |
| |
| if test.expectedOffset! >= max { |
| expectCrashLater() |
| } |
| let new = c.index( |
| c.nthIndex(test.startOffset), |
| offsetBy: numericCast(test.distance)) |
| |
| // Since the `nthIndex(offset:)` method performs the same operation |
| // (i.e. advances `c.startIndex` by `test.distance`, it would be |
| // silly to compare index values. Luckily the underlying collection |
| // contains exactly index offsets. |
| expectEqual(test.expectedOffset!, extractValue(c[new]).value, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| |
| self.test("\(testNamePrefix)/formIndex(_:offsetBy: n)/semantics") |
| .forEach( |
| in: indexOffsetByTests.filter { $0.limit == nil && $0.distance >= 0 } |
| ) { |
| test in |
| let max = 10 |
| let c = toCollection(0..<max) |
| |
| var new = c.nthIndex(test.startOffset) |
| |
| if test.expectedOffset! >= max { |
| expectCrashLater() |
| } |
| c.formIndex(&new, offsetBy: numericCast(test.distance)) |
| expectEqual(test.expectedOffset!, extractValue(c[new]).value, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| |
| % for function_name in ['index', 'formIndex']: |
| self.test("\(testNamePrefix)/${function_name}(_:offsetBy: -n)/semantics") |
| .forEach( |
| in: indexOffsetByTests.filter { $0.limit == nil && $0.distance < 0 } |
| ) { |
| test in |
| let c = toCollection(0..<20) |
| % if function_name is 'index': |
| let start = c.nthIndex(test.startOffset) |
| % else: |
| var new = c.nthIndex(test.startOffset) |
| % end |
| |
| if test.expectedOffset! < 0 || !collectionIsBidirectional { |
| expectCrashLater() |
| } |
| % if function_name is 'index': |
| let new = c.index(start, offsetBy: numericCast(test.distance)) |
| % else: |
| c.formIndex(&new, offsetBy: numericCast(test.distance)) |
| % end |
| expectEqual(test.expectedOffset!, extractValue(c[new]).value, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } |
| % end |
| |
| self.test("\(testNamePrefix)/index(_:offsetBy: n, limitedBy:)/semantics") { |
| for test in indexOffsetByTests.filter( |
| {$0.limit != nil && $0.distance >= 0} |
| ) { |
| let c = toCollection(0..<20) |
| let limit = c.nthIndex(test.limit.unsafelyUnwrapped) |
| let new = c.index( |
| c.nthIndex(test.startOffset), |
| offsetBy: numericCast(test.distance), |
| limitedBy: limit) |
| if let expectedOffset = test.expectedOffset { |
| expectEqual(c.nthIndex(expectedOffset), new!, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } else { |
| expectNil(new) |
| } |
| } |
| } |
| |
| self.test("\(testNamePrefix)/formIndex(_:offsetBy: n, limitedBy:)/semantics") { |
| for test in indexOffsetByTests.filter( |
| {$0.limit != nil && $0.distance >= 0} |
| ) { |
| let c = toCollection(0..<20) |
| let limit = c.nthIndex(test.limit.unsafelyUnwrapped) |
| var new = c.nthIndex(test.startOffset) |
| let exact = c.formIndex(&new, offsetBy: numericCast(test.distance), limitedBy: limit) |
| if let expectedOffset = test.expectedOffset { |
| expectEqual(c.nthIndex(expectedOffset), new, |
| stackTrace: SourceLocStack().with(test.loc)) |
| expectTrue(exact, stackTrace: SourceLocStack().with(test.loc)) |
| } else { |
| // Clamped to the limit |
| expectEqual(limit, new, stackTrace: SourceLocStack().with(test.loc)) |
| expectFalse(exact, stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } |
| } |
| |
| self.test("\(testNamePrefix)/index(_:offsetBy: -n, limitedBy:)/semantics") |
| .forEach( |
| in: indexOffsetByTests.filter { $0.limit != nil && $0.distance < 0 } |
| ) { |
| test in |
| let c = toCollection(0..<20) |
| let limit = c.nthIndex(test.limit.unsafelyUnwrapped) |
| if collectionIsBidirectional { |
| let new = c.index( |
| c.nthIndex(test.startOffset), |
| offsetBy: numericCast(test.distance), |
| limitedBy: limit) |
| if let expectedOffset = test.expectedOffset { |
| expectEqual(c.nthIndex(expectedOffset), new!, |
| stackTrace: SourceLocStack().with(test.loc)) |
| } else { |
| expectNil(new) |
| } |
| } else { |
| expectCrashLater() |
| _ = c.index( |
| c.nthIndex(test.startOffset), |
| offsetBy: numericCast(test.distance), |
| limitedBy: limit) |
| } |
| } |
| |
| self.test("\(testNamePrefix)/formIndex(_:offsetBy: -n, limitedBy:)/semantics") |
| .forEach( |
| in: indexOffsetByTests.filter { $0.limit != nil && $0.distance < 0 } |
| ) { |
| test in |
| let c = toCollection(0..<20) |
| let limit = c.nthIndex(test.limit.unsafelyUnwrapped) |
| var new = c.nthIndex(test.startOffset) |
| if collectionIsBidirectional { |
| let exact = c.formIndex( |
| &new, |
| offsetBy: numericCast(test.distance), |
| limitedBy: limit) |
| if let expectedOffset = test.expectedOffset { |
| expectEqual(c.nthIndex(expectedOffset), new, |
| stackTrace: SourceLocStack().with(test.loc)) |
| expectTrue(exact, stackTrace: SourceLocStack().with(test.loc)) |
| } else { |
| expectEqual(limit, new, stackTrace: SourceLocStack().with(test.loc)) |
| expectFalse(exact, stackTrace: SourceLocStack().with(test.loc)) |
| } |
| } else { |
| expectCrashLater() |
| _ = c.formIndex(&new, offsetBy: numericCast(test.distance), limitedBy: limit) |
| } |
| } |
| |
| } |
| % end |
| } |