//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2021 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 // //===----------------------------------------------------------------------===// // Loosely adapted from https://github.com/apple/swift/tree/main/stdlib/private/StdlibCollectionUnittest // FIXME: Port all of the collection validation tests from the Swift compiler codebase. import XCTest // FIXME: Port the collection validation tests from the Swift compiler codebase. extension Sequence { func _contentsByIterator() -> [Element] { var result: [Element] = [] var it = makeIterator() while let item = it.next() { result.append(item) } return result } func _contentsByCopyContents(_ count: Int? = nil) -> [Element] { var it: Iterator? var result = Array( unsafeUninitializedCapacity: count ?? self.underestimatedCount ) { buffer, count in (it, count) = self._copyContents(initializing: buffer) } while let next = it!.next() { result.append(next) } return result } } extension Collection { func _indicesByIndexAfter() -> [Index] { var result: [Index] = [] var i = startIndex while i != endIndex { result.append(i) i = index(after: i) } return result } func _indicesByFormIndexAfter() -> [Index] { var result: [Index] = [] var i = startIndex while i != endIndex { result.append(i) formIndex(after: &i) } return result } } public func checkCollection( _ collection: C, expectedContents: Expected, file: StaticString = #file, line: UInt = #line ) where C.Element: Equatable, Expected.Element == C.Element { let expectedContents = Array(expectedContents) checkCollection( collection, expectedContents: expectedContents, by: ==, file: file, line: line) let indicesByIndexAfter = collection._indicesByIndexAfter() for item in expectedContents { if let first = collection._customIndexOfEquatableElement(item) { expectNotNil(first) { first in expectEqual(collection[first], item) let expected = expectedContents.firstIndex(of: item)! expectEqual(first, indicesByIndexAfter[expected]) } } if let last = collection._customLastIndexOfEquatableElement(item) { expectNotNil(last) { last in expectEqual(collection[last], item) let expected = expectedContents.lastIndex(of: item)! expectEqual(last, indicesByIndexAfter[expected]) } } } } public func checkCollection( _ collection: C, expectedContents: Expected, by areEquivalent: (C.Element, C.Element) -> Bool, file: StaticString = #file, line: UInt = #line ) where Expected.Element == C.Element { checkSequence( { collection }, expectedContents: expectedContents, by: areEquivalent, file: file, line: line) _checkCollection( collection, expectedContents: expectedContents, by: areEquivalent, file: file, line: line) } public func _checkCollection( _ collection: C, expectedContents: Expected, by areEquivalent: (C.Element, C.Element) -> Bool, file: StaticString = #file, line: UInt = #line ) where Expected.Element == C.Element { let entry = TestContext.current.push("checkCollection", file: file, line: line) defer { TestContext.current.pop(entry) } let expectedContents = Array(expectedContents) expectEqual(collection.isEmpty, expectedContents.isEmpty) expectEqual(collection.count, expectedContents.count) // Check that `index(after:)` produces the same results as `formIndex(after:)` let indicesByIndexAfter = collection._indicesByIndexAfter() let indicesByFormIndexAfter = collection._indicesByFormIndexAfter() expectEqual(indicesByIndexAfter, indicesByFormIndexAfter) // Check contents using the iterator. expectEquivalentElements( collection._contentsByIterator(), expectedContents, by: areEquivalent) // Check _copyContents. expectEquivalentElements( collection._contentsByCopyContents(), expectedContents, by: areEquivalent) // Check contents using indexing. let indexContents1 = indicesByIndexAfter.map { collection[$0] } expectEquivalentElements(indexContents1, expectedContents, by: areEquivalent) let indexContents2 = indicesByFormIndexAfter.map { collection[$0] } expectEquivalentElements(indexContents2, expectedContents, by: areEquivalent) // Check the endIndex. expectEqual(collection.endIndex, collection.indices.endIndex) // Check the Indices associated type if C.self != C.Indices.self { checkCollection(collection.indices, expectedContents: indicesByIndexAfter) } else { expectEqual(collection.indices.count, collection.count) expectEqualElements(collection.indices, indicesByIndexAfter) } expectEqual(collection.indices.endIndex, collection.endIndex) // The sequence of indices must be monotonically increasing. var allIndices = indicesByIndexAfter allIndices.append(collection.endIndex) checkComparable(allIndices, oracle: { .comparing($0, $1) }) // Check `index(_,offsetBy:)` for (offset, start) in allIndices.enumerated() { for distance in 0 ... indicesByIndexAfter.count - offset { let end = collection.index(start, offsetBy: distance) expectEqual(end, allIndices[offset + distance]) if offset + distance < expectedContents.count { expectEquivalent(collection[end], expectedContents[offset + distance], by: areEquivalent) } } } // Check `index(_,offsetBy:limitedBy:)` let limits = Set([0, allIndices.count - 1, allIndices.count / 2]) .sorted() withEvery("limit", in: limits) { limit in withEvery("i", in: 0 ..< allIndices.count) { i in let max = allIndices.count - i + (limit >= i ? 2 : 0) withEvery("delta", in: 0 ..< max) { delta in let actual = collection.index( allIndices[i], offsetBy: delta, limitedBy: allIndices[limit]) let j = i + delta let expected = i > limit || j <= limit ? allIndices[j] : nil expectEqual(actual, expected) } } } // Check `distance(from:to:)` withEvery("i", in: allIndices.indices) { i in withEvery("j", in: allIndices.indices[i...]) { j in let d = collection.distance(from: allIndices[i], to: allIndices[j]) expectEqual(d, j - i) } } // Check slicing. withEvery("i", in: 0 ..< allIndices.count) { i in withEvery("j", in: i ..< allIndices.count) { j in let range = allIndices[i] ..< allIndices[j] let slice = collection[range] expectEqual(slice.count, j - i) expectEqual(slice.isEmpty, i == j) expectEqual(slice.startIndex, allIndices[i]) expectEqual(slice.endIndex, allIndices[j]) expectEqual(slice.distance(from: allIndices[i], to: allIndices[j]), j - i) expectEqual(slice.index(allIndices[i], offsetBy: j - i), allIndices[j]) expectEqual(slice.index(allIndices[i], offsetBy: j - i, limitedBy: allIndices[j]), allIndices[j]) expectEqual(slice.index(allIndices[i], offsetBy: j - i, limitedBy: allIndices[i]), j - i > 0 ? nil : allIndices[i]) expectEqual(slice.index(allIndices[i], offsetBy: j - i, limitedBy: allIndices[0]), i > 0 || j == 0 ? allIndices[j] : nil) expectEquivalentElements(slice, expectedContents[i ..< j], by: areEquivalent) expectEquivalentElements( slice._contentsByIterator(), expectedContents[i ..< j], by: areEquivalent) expectEquivalentElements( slice._contentsByCopyContents(), expectedContents[i ..< j], by: areEquivalent) // Check _copyContents. let copyContents = collection._contentsByCopyContents() expectEquivalentElements( copyContents, expectedContents, by: areEquivalent) expectEqualElements(slice._indicesByIndexAfter(), allIndices[i ..< j]) expectEqualElements(slice._indicesByFormIndexAfter(), allIndices[i ..< j]) expectEqualElements(slice.indices, allIndices[i ..< j]) expectEqualElements(slice.indices._indicesByIndexAfter(), allIndices[i ..< j]) expectEqualElements(slice.indices._indicesByFormIndexAfter(), allIndices[i ..< j]) // Check the subsequence iterator. expectEquivalentElements( slice, expectedContents[i ..< j], by: areEquivalent) // Check the subsequence subscript. expectEquivalentElements( allIndices[i ..< j].map { slice[$0] }, expectedContents[i ..< j], by: areEquivalent) // Check _copyToContiguousArray. expectEquivalentElements( Array(slice), expectedContents[i ..< j], by: areEquivalent) // Check slicing of slices. expectEquivalentElements( slice[range], expectedContents[i ..< j], by: areEquivalent) } } }