Files
swift-mirror/stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift.gyb
Dmitri Gribenko aaa486cc7b New indexing model: fix most compilation issues in StdlibCollectionUnittest
We are still hitting what looks like a compiler bug:

error: type alias 'SubSequence' circularly references itself

which is emitted for every minimal collection type.
2016-03-18 23:20:57 -07:00

540 lines
17 KiB
Swift

//===--- 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, TRACE1, stackTrace, trace
from gyb_stdlib_support import TRAVERSALS, collectionForTraversal
}%
import StdlibUnittest
public struct CollectionMisuseResiliencyChecks {
public enum FailureKind {
case none
case trap
case expectationFailure
}
public var callNextOnExhaustedGenerator: Bool = true
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(
callNextOnExhaustedGenerator: false,
creatingOutOfBoundsIndicesBehavior: .none,
subscriptOnOutOfBoundsIndicesBehavior: .none,
subscriptRangeOnOutOfBoundsRangesBehavior: .none)
}
}
// TODO: swift-3-indexing-model - review the following
#if false
% for inc, protocol, successor, end in (
% ('inc', '_Incrementable', 'successor', 'end'),
% ('dec', 'BidirectionalIndex', 'predecessor', '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 : Collection
where Instances.Iterator.Element : ${protocol}
>(
instances: Instances,
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
${end}Index: Instances.Iterator.Element, ${TRACE}
) {
checkEquatable(instances, oracle: equalityOracle, ${trace})
for i in instances {
if i != ${end}Index {
let next = i.${successor}()
// ${successor} gets us a new index value
expectNotEqual(i, next, ${trace})
// Which is the same as if we apply _${successor}InPlace
var j = i
j._${successor}InPlace()
expectEqual(j, next, ${trace})
}
}
}
%end
#endif
// TODO: swift-3-indexing-model - review the following
#if false
internal func _checkIncrementalAdvance<
Instances : Collection
where
Instances.Iterator.Element : ForwardIndex
>(
instances: Instances,
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
limit: Instances.Iterator.Element,
sign: Instances.Iterator.Element.Distance, // 1 or -1
next: (Instances.Iterator.Element) -> Instances.Iterator.Element,
${TRACE}
) {
for i in instances {
// TODO: swift-3-indexing-model - Review what we need to do here, unittest is focusing on wrong class now
let d: Instances.Iterator.Element.Distance = 0 //sign > 0 ? i.distance(to: limit) : -limit.distance(to: i)
var offset: Instances.Iterator.Element.Distance = 0
for _ in 0...(d * sign).toIntMax() {
// TODO: swift-3-indexing-model - Review what we need to do here, unittest is focusing on wrong class now
let j = i//.advanced(by: offset)
let k = i//.advanced(by: offset + sign, 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 `ForwardIndex`, 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 : Collection
where
Instances.Iterator.Element : ForwardIndex
>(
instances: Instances,
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
endIndex: Instances.Iterator.Element, ${TRACE}
) {
typealias Index = Instances.Iterator.Element
checkIncrementable(
instances, equalityOracle: equalityOracle, endIndex: endIndex, ${trace})
_checkIncrementalAdvance(
instances, equalityOracle: equalityOracle, limit: endIndex,
sign: 1, next: { $0.successor() }, ${trace})
}
/// Test that the elements of `instances` satisfy the semantic
/// requirements of `BidirectionalIndex`, 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: Collection
where
Instances.Iterator.Element : BidirectionalIndex
>(
instances: Instances,
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
startIndex: Instances.Iterator.Element,
endIndex: Instances.Iterator.Element,
${TRACE}
) {
typealias Index = Instances.Iterator.Element
checkForwardIndex(
instances, equalityOracle: equalityOracle, endIndex: endIndex)
checkDecrementable(
instances, equalityOracle: equalityOracle, startIndex: startIndex, ${trace})
_checkIncrementalAdvance(
instances, equalityOracle: equalityOracle, limit: startIndex,
sign: -1, next: { $0.predecessor() }, ${trace})
}
/// Test that the elements of `instances` satisfy the semantic
/// requirements of `RandomAccessIndex`, 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 : Collection, Distances : Collection
where
Instances.Iterator.Element : RandomAccessIndex,
Distances.Iterator.Element == Instances.Iterator.Element.Distance,
Instances.Iterator.Element.Distance == Instances.Iterator.Element.Stride
>(
instances: Instances, distances: Distances,
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}
) {
checkBidirectionalIndex(
instances, equalityOracle: { distanceOracle($0, $1) == 0 },
startIndex: startIndex, endIndex: endIndex, ${trace})
checkStrideable(
instances, strides: distances,
distanceOracle: distanceOracle,
advanceOracle: advanceOracle, ${trace})
}
// 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 zip(
% ('Expected: Collection', 'Element'),
% ('Expected.Iterator.Element', 'Element'),
% ('Expected', 'Array<Element>')):
%for Traversal in TRAVERSALS:
public func check${Traversal}Collection<
${genericParam}, C : Collection
where
C.Iterator.Element == ${Element},
C.Index : ${Traversal}Index
% if Traversal == 'RandomAccess':
, C.Index.Stride == C.Index.Distance
% end
>(
expected: ${Expected}, _ collection: C,
${TRACE},
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
sameValue: (${Element}, ${Element}) -> Bool
) {
// A `Collection` is a multi-pass `Sequence`.
for _ in 0..<3 {
checkSequence(
expected, collection, ${trace},
resiliencyChecks: resiliencyChecks, sameValue: sameValue)
}
// 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 : $0.successor() }
// 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':
checkForwardIndex(
collection.startIndex..<partWay0, equalityOracle: { $0 == $1 },
endIndex: partWay1, ${trace})
% elif Traversal == 'Bidirectional':
checkBidirectionalIndex(
partWay0..<partWay1, equalityOracle: { $0 == $1 },
startIndex: collection.startIndex,
endIndex: next5(partWay1), ${trace})
% else:
% assert(Traversal == 'RandomAccess')
typealias Distance = C.Index.Distance
let count: Distance = collection.count
let offset0 = min(5, count)
let offset1 = min(10, count)
let offset2 = min(15, count)
let distanceCandidates: [Distance] = [
-11, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 11]
let distances = distanceCandidates.filter { (x: Distance) -> Bool in
x + offset0 >= 0 && x + offset1 <= count
}
func nextN(n: C.Index.Distance, _ i: C.Index) -> C.Index {
var i = i
if n < 0 {
for _ in 0 ..< -(n.toIntMax()) {
i -= 1
}
}
else {
for _ in 0..<n.toIntMax() {
i += 1
}
}
return i
}
let instances = Array(partWay0..<partWay1)
typealias Distances = [Distance]
checkRandomAccessIndex(
instances,
distances: distances,
distanceOracle: { (x:Int,y:Int) in Distance(IntMax(y - x)) },
advanceOracle: { x,y in nextN(distances[y], instances[x]) },
startIndex: collection.startIndex,
endIndex: next5(partWay1), ${trace})
% end
let expectedArray = Array(expected)
expectEqual(
expectedArray.count.toIntMax(), collection.count.toIntMax(), ${trace})
for _ in 0..<3 {
if true {
let startIndex = collection.startIndex
let endIndex = collection.endIndex
for _ in collection.indices {
expectEqual(
startIndex, collection.startIndex,
"Iteration should not change startIndex",
stackTrace: ${stackTrace})
expectEqual(
endIndex, collection.endIndex,
"Iteration should not change endIndex",
stackTrace: ${stackTrace})
}
}
var allIndices = Array(collection.indices)
if expectedArray.count >= 2 {
for i in 0..<allIndices.count-1 {
let successor1 = allIndices[i].successor()
var successor2 = allIndices[i]
successor2 = successor2.successor()
var successor3 = allIndices[i]
successor3 = successor3.successor()
for s in [ successor1, successor2, successor3 ] {
expectEqual(allIndices[i + 1], s, ${trace})
expectEqual(
expectedArray[i + 1], collection[s], ${trace}, sameValue: sameValue)
}
}
% if Traversal == "Bidirectional":
for i in 1..<allIndices.count {
let predecessor1 = allIndices[i].predecessor()
var predecessor2 = allIndices[i]
predecessor2 = predecessor2.predecessor()
var predecessor3 = allIndices[i]
predecessor3 = predecessor3.predecessor()
for p in [ predecessor1, predecessor2, predecessor3 ] {
expectEqual(allIndices[i - 1], p, ${trace})
expectEqual(
expectedArray[i - 1], collection[p], ${trace}, sameValue: sameValue)
}
}
for i in 1..<allIndices.count {
var index = allIndices[i]
index = index.predecessor()
index = index.successor()
expectEqual(allIndices[i], index, ${trace})
expectEqual(
expectedArray[i], collection[index], ${trace}, sameValue: sameValue)
}
% end
}
if true {
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)
}
}
// FIXME: more checks for bidirectional and random access collections.
}
public func check${Traversal}Collection<
${genericParam}, C : Collection
where
C.Iterator.Element == ${Element},
C.Index : ${Traversal}Index,
${Element} : Equatable
% if Traversal == 'RandomAccess':
, C.Index.Stride == C.Index.Distance
% end
>(
expected: ${Expected}, _ collection: C,
${TRACE},
resiliencyChecks: CollectionMisuseResiliencyChecks = .all
) {
check${Traversal}Collection(
expected, collection, ${trace},
resiliencyChecks: resiliencyChecks) { $0 == $1 }
}
% end
// FIXME: merge into checkCollection()
public func checkSliceableWithBidirectionalIndex<
${genericParam}, S : Collection
where
S.Iterator.Element == ${Element},
S.SubSequence.Iterator.Element == ${Element},
S.Index : BidirectionalIndex,
S.SubSequence : Collection,
S.SubSequence.Index : BidirectionalIndex,
${Element} : Equatable
>(
expected: ${Expected}, _ sliceable: S, ${TRACE}
) {
// A `Sliceable` is a `Collection`.
checkBidirectionalCollection(expected, sliceable, ${trace})
let expectedArray = Array(expected)
var start = sliceable.startIndex
for startNumericIndex in 0...expectedArray.count {
if start != sliceable.endIndex {
start = start.successor()
start = start.predecessor()
start = start.successor()
start = start.predecessor()
}
var end = start
for endNumericIndex in startNumericIndex...expectedArray.count {
if end != sliceable.endIndex {
end = end.successor()
end = end.predecessor()
end = end.successor()
end = end.predecessor()
}
let expectedSlice = expectedArray[startNumericIndex..<endNumericIndex]
let slice = sliceable[start..<end]
checkBidirectionalCollection(expectedSlice, slice, ${trace})
if end != sliceable.endIndex {
end = end.successor()
}
}
if start != sliceable.endIndex {
start = start.successor()
}
}
}
% end
#endif
public func checkRangeReplaceable<
C : RangeReplaceableCollection,
N : Collection
where
C.Iterator.Element : Equatable,
C.Iterator.Element == N.Iterator.Element,
C.Indices.Iterator.Element == C.Index
>(
makeCollection: () -> C,
_ makeNewValues: (Int) -> N
) {
typealias A = C
// First make an independent copy of the array that we can use for
// comparison later.
let source = Array<A.Iterator.Element>(makeCollection())
for (ix, i) in source.indices.enumerated() {
for (jx_, j) in (i..<source.endIndex).enumerated() {
let jx = jx_ + ix
let oldCount = jx - ix
for newCount in 0..<(2 * oldCount) {
let newValues = makeNewValues(newCount)
func reportFailure(a: inout A, _ message: String) {
print("\(message) when replacing indices \(ix)...\(jx)")
print(" in \(Array(source)) with \(Array(newValues))")
print(" yielding \(Array(a))")
print("====================================")
expectTrue(false)
}
var a = makeCollection()
a.replaceSubrange(nthIndex(a, ix)..<nthIndex(a, jx), with: newValues)
let growth = newCount - oldCount
let expectedCount = source.count + growth
let actualCount = numericCast(a.count) as Int
if actualCount != expectedCount {
reportFailure(
&a, "\(actualCount) != expected count \(expectedCount)")
}
for (kx, k) in a.indices.enumerated() {
let expectedValue = kx < ix ? nth(source, kx)
: kx < jx + growth ? nth(newValues, kx - ix)
: nth(source, kx - growth)
if a[k] != expectedValue {
reportFailure(
&a,
// FIXME: why do we need to break this string into two parts?
"a[\(kx)] = "
+ "\(a[k]) != expected value \(expectedValue)")
}
}
}
}
}
}
public func checkCollection<
C : Collection,
Expected : Collection
where
C.Iterator.Element == Expected.Iterator.Element
>(
subject: C,
expected: Expected,
${TRACE},
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
sameValue: (Expected.Iterator.Element, Expected.Iterator.Element) -> Bool
) {
}