//===--- CheckCollectionInstance.swift.gyb --------------------*- swift -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// %{ from gyb_stdlib_unittest_support import TRACE, stackTrace, trace from gyb_stdlib_support import TRAVERSALS, collectionForTraversal }% import StdlibUnittest public struct CollectionMisuseResiliencyChecks { public enum FailureKind { case none case trap } public var creatingOutOfBoundsIndicesBehavior: FailureKind = .trap public var subscriptOnOutOfBoundsIndicesBehavior: FailureKind = .trap public var subscriptRangeOnOutOfBoundsRangesBehavior: FailureKind = .trap public static var all: CollectionMisuseResiliencyChecks { return CollectionMisuseResiliencyChecks() } public static var none: CollectionMisuseResiliencyChecks { return CollectionMisuseResiliencyChecks( creatingOutOfBoundsIndicesBehavior: .none, subscriptOnOutOfBoundsIndicesBehavior: .none, subscriptRangeOnOutOfBoundsRangesBehavior: .none) } } % for inc, protocol, direction, end in ( % ('inc', 'Indexable', 'after', 'end'), % ('dec', 'BidirectionalIndexable', 'before', 'start')): /// Test that the elements of `instances` satisfy /// ${'some of ' if inc == 'dec' else ''}the semantic /// requirements of `${protocol}`, using `equalityOracle` to /// generate equality expectations from pairs of positions in /// `instances`. /// /// - Precondition: ${'''`endIndex` is reachable from all /// elements of `instances`.''' if inc == 'inc' else '''all /// elements of `instances` are reachable from `startIndex`.'''} public func check${inc.capitalize()}rementable( _ instances: Instances, of baseCollection: BaseCollection, equalityOracle: (Instances.Index, Instances.Index) -> Bool, ${end}Index: Instances.Iterator.Element, ${TRACE} ) where Instances : Collection, BaseCollection : ${protocol}, Instances.Iterator.Element == BaseCollection.Index, // FIXME(compiler limitation): these constraints should be applied to // associated types of Collection. Instances.Indices.Iterator.Element == Instances.Index { checkEquatable(instances, oracle: equalityOracle, ${trace}) for i in instances { if i != ${end}Index { let next = baseCollection.index(${direction}: i) // index(${direction}:) gets us a new index value expectNotEqual(i, next, ${trace}) // Which is the same as if we apply formIndex(${direction}:) var j = i baseCollection.formIndex(${direction}: &j) expectEqual(j, next, ${trace}) } } } %end internal func _checkIncrementalAdvance( _ instances: Instances, of baseCollection : BaseCollection, equalityOracle: (Instances.Index, Instances.Index) -> Bool, limit: Instances.Iterator.Element, sign: BaseCollection.IndexDistance, // 1 or -1 next: (Instances.Iterator.Element) -> Instances.Iterator.Element, ${TRACE} ) where Instances : Collection, BaseCollection : Indexable, Instances.Iterator.Element == BaseCollection.Index, // FIXME(compiler limitation): these constraints should be applied to // associated types of Collection. Instances.Indices.Iterator.Element == Instances.Index { for i in instances { let d: BaseCollection.IndexDistance = sign > 0 ? baseCollection.distance(from: i, to: limit) : -baseCollection.distance(from: limit, to: i) var offset: BaseCollection.IndexDistance = 0 for _ in 0...(d * sign).toIntMax() { let j = baseCollection.index(i, offsetBy: offset) let k = baseCollection.index(i, offsetBy: offset + sign, limitedBy: limit) ?? limit let jAtLimit = offset == d if jAtLimit { expectEqual(limit, j, ${trace}) } expectEqual(jAtLimit ? j : next(j), k, ${trace}) offset += sign } } } /// Test that the elements of `instances` satisfy the semantic requirements of /// index for `Collection`, using `equalityOracle` to generate equality /// expectations from pairs of positions in `instances`. /// /// - Precondition: `endIndex` is reachable from all elements of /// `instances` public func checkForwardIndex( _ instances: Instances, of baseCollection: BaseCollection, equalityOracle: (Instances.Index, Instances.Index) -> Bool, endIndex: Instances.Iterator.Element, ${TRACE} ) where Instances : Collection, BaseCollection : Indexable, Instances.Iterator.Element == BaseCollection.Index, // FIXME(compiler limitation): these constraints should be applied to // associated types of Collection. Instances.Indices.Iterator.Element == Instances.Index { checkIncrementable(instances, of: baseCollection, equalityOracle: equalityOracle, endIndex: endIndex, ${trace}) _checkIncrementalAdvance(instances, of: baseCollection, equalityOracle: equalityOracle, limit: endIndex, sign: 1, next: { baseCollection.index(after: $0) }, ${trace}) } /// Test that the elements of `instances` satisfy the semantic requirements of /// index for `BidirectionalCollection`, using `equalityOracle` to generate /// equality expectations from pairs of positions in `instances`. /// /// - Precondition: /// - all elements of `instances` are reachable from `startIndex`. /// - `endIndex` is reachable from all elements of `instances`. public func checkBidirectionalIndex( _ instances: Instances, of baseCollection: BaseCollection, equalityOracle: (Instances.Index, Instances.Index) -> Bool, startIndex: Instances.Iterator.Element, endIndex: Instances.Iterator.Element, ${TRACE} ) where Instances: Collection, BaseCollection : BidirectionalIndexable, Instances.Iterator.Element == BaseCollection.Index, // FIXME(compiler limitation): these constraints should be applied to // associated types of Collection. Instances.Indices.Iterator.Element == Instances.Index { checkForwardIndex(instances, of: baseCollection, equalityOracle: equalityOracle, endIndex: endIndex) checkDecrementable(instances, of: baseCollection, equalityOracle: equalityOracle, startIndex: startIndex, ${trace}) _checkIncrementalAdvance(instances, of: baseCollection, equalityOracle: equalityOracle, limit: startIndex, sign: -1, next: { baseCollection.index(before: $0) }, ${trace}) } /// Test that the elements of `instances` satisfy the semantic requirements of /// index for `RandomAccessCollection`, using `advanceOracle` and /// 'distanceOracle' to generate expectations about the results of /// `advanced(by:)` and `distance(to:)` from pairs of positions in `instances` /// and `distances`. /// /// - Precondition: /// - all elements of `instances` are reachable from `startIndex`. /// - `endIndex` is reachable from all elements of `instances`. public func checkRandomAccessIndex( _ instances: Instances, distances: Distances, of baseCollection: BaseCollection, distanceOracle: (Instances.Index, Instances.Index) -> Distances.Iterator.Element, advanceOracle: (Instances.Index, Distances.Index) -> Instances.Iterator.Element, startIndex: Instances.Iterator.Element, endIndex: Instances.Iterator.Element, ${TRACE} ) where Instances : Collection, Distances : Collection, BaseCollection : RandomAccessIndexable, Instances.Iterator.Element == BaseCollection.Index, Distances.Iterator.Element == BaseCollection.IndexDistance, // FIXME(compiler limitation): these constraints should be applied to // associated types of Collection. Instances.Indices.Iterator.Element == Instances.Index, Distances.Indices.Iterator.Element == Distances.Index { checkBidirectionalIndex(instances, of: baseCollection, equalityOracle: { distanceOracle($0, $1) == 0 }, startIndex: startIndex, endIndex: endIndex, ${trace}) checkAdvancesAndDistances( instances, distances: distances, of: baseCollection, distanceOracle: distanceOracle, advanceOracle: advanceOracle, ${trace}) } // Copies what checkStrideable is doing, but instead of calling // advanced(by:) and distance(to:) on an Strideable's, // calls corresponding methods on a base collection. public func checkAdvancesAndDistances( _ instances: Instances, distances: Distances, of baseCollection: BaseCollection, distanceOracle: (Instances.Index, Instances.Index) -> Distances.Iterator.Element, advanceOracle: (Instances.Index, Distances.Index) -> Instances.Iterator.Element, ${TRACE} ) where Instances : Collection, Distances : Collection, BaseCollection : Indexable, Instances.Iterator.Element == BaseCollection.Index, Distances.Iterator.Element == BaseCollection.IndexDistance, // FIXME(compiler limitation): these constraints should be applied to // associated types of Collection. Instances.Indices.Iterator.Element == Instances.Index, Distances.Indices.Iterator.Element == Distances.Index { checkComparable( instances, oracle: { let d = distanceOracle($1, $0); return d < 0 ? .lt : d == 0 ? .eq : .gt }, ${trace}) for i in instances.indices { let x = instances[i] expectEqual(x, baseCollection.index(x, offsetBy: 0)) for j in distances.indices { let y = distances[j] expectEqual(advanceOracle(i, j), baseCollection.index(x, offsetBy: y)) } for j in instances.indices { let y = instances[j] expectEqual(distanceOracle(i, j), baseCollection.distance(from: x, to: y)) } } } // Generate two overloads: one for Array (which will get // picked up when the caller passes a literal), and another that // accepts any appropriate Collection type. % for genericParam, Element, Expected in [ % ('Expected: Collection', 'Expected.Iterator.Element', 'Expected'), % ('Element' , 'Element' , 'Array')]: % for Traversal in TRAVERSALS: % TraversalCollection = collectionForTraversal(Traversal) public func check${Traversal}Collection< ${genericParam}, C : ${TraversalCollection} >( _ expected: ${Expected}, _ collection: C, ${TRACE}, resiliencyChecks: CollectionMisuseResiliencyChecks = .all, sameValue: (${Element}, ${Element}) -> Bool ) where C.Iterator.Element == ${Element}, C.Indices.Iterator.Element == C.Index { // A `Collection` is a multi-pass `Sequence`. for _ in 0..<3 { checkSequence( expected, collection, ${trace}, resiliencyChecks: resiliencyChecks, sameValue: sameValue) } let succ = { collection.index(after: $0) } % if Traversal != 'Forward': let pred = { collection.index(before: $0) } % end // Advances up to 1 positions without passing endIndex. Don't use // advanced(by: n) to do this because it's under test here. let next = { $0 == collection.endIndex ? $0 : succ($0) } // advances up to 5 positions without passing endIndex. Picking a // small constant to avoid complexity explosion on large input // collections. let next5 = { next(next(next(next(next($0))))) } let partWay0 = next5(collection.startIndex) let partWay1 = next5(partWay0) % if Traversal == 'Forward': let instances = _allIndices(into: collection, in: collection.startIndex.. Bool in x + offset0 >= 0 && x + offset1 <= count } func nextN(_ n: Distance, _ i: C.Index) -> C.Index { return collection.index(i, offsetBy: n) } let instances = _allIndices(into: collection, in: partWay0..= 2 { for i in 0..= 2` do { var allIndices2: [C.Index] = [] for i in collection.indices { allIndices2.append(i) } expectEqualSequence( allIndices, allIndices2, "iteration should not invalidate indices", stackTrace: ${stackTrace}) expectEqualSequence( expectedArray, allIndices.map { collection[$0] }, stackTrace: ${stackTrace}, sameValue: sameValue) expectEqualSequence( expectedArray, allIndices2.map { collection[$0] }, stackTrace: ${stackTrace}, sameValue: sameValue) } } // end of `for _ in 0..<3` // FIXME: more checks for bidirectional and random access collections. } public func check${Traversal}Collection< ${genericParam}, C : ${TraversalCollection} >( _ expected: ${Expected}, _ collection: C, ${TRACE}, resiliencyChecks: CollectionMisuseResiliencyChecks = .all ) where C.Iterator.Element == ${Element}, C.Indices.Iterator.Element == C.Index, ${Element} : Equatable { check${Traversal}Collection( expected, collection, ${trace}, resiliencyChecks: resiliencyChecks) { $0 == $1 } } % end // FIXME: merge into checkCollection() public func checkSliceableWithBidirectionalIndex< ${genericParam}, S : BidirectionalCollection >( _ expected: ${Expected}, _ sliceable: S, ${TRACE} ) where S.Iterator.Element == ${Element}, S.Indices.Iterator.Element == S.Index, S.SubSequence : BidirectionalCollection, S.SubSequence.Iterator.Element == ${Element}, S.SubSequence.Indices.Iterator.Element == S.SubSequence.Index, ${Element} : Equatable { // A `Sliceable` is a `Collection`. checkBidirectionalCollection(expected, sliceable, ${trace}) let expectedArray = Array(expected) let succ = { sliceable.index(after: $0) } let pred = { sliceable.index(before: $0) } var start = sliceable.startIndex for startNumericIndex in 0...expectedArray.count { if start != sliceable.endIndex { start = succ(start) start = pred(start) start = succ(start) start = pred(start) } var end = start for endNumericIndex in startNumericIndex...expectedArray.count { if end != sliceable.endIndex { end = succ(end) end = pred(end) end = succ(end) end = pred(end) } let expectedSlice = expectedArray[startNumericIndex..( _ makeCollection: @escaping () -> C, _ makeNewValues: (Int) -> N ) where C : RangeReplaceableCollection, N : Collection, C.Iterator.Element : Equatable, C.Iterator.Element == N.Iterator.Element, C.Indices.Iterator.Element == C.Index { typealias A = C // First make an independent copy of the array that we can use for // comparison later. let source = Array(makeCollection()) for (ix, i) in source.indices.enumerated() { for (jx_, j) in (i..( _ subject: C, expected: Expected, ${TRACE}, resiliencyChecks: CollectionMisuseResiliencyChecks = .all, sameValue: (Expected.Iterator.Element, Expected.Iterator.Element) -> Bool ) where C.Iterator.Element == Expected.Iterator.Element { }