Skip to content

Commit f8b97c5

Browse files
committed
Merge pull request #2339 from austinzheng/validation-tests
2 parents 16b3d6c + e9c1db2 commit f8b97c5

File tree

2 files changed

+180
-35
lines changed

2 files changed

+180
-35
lines changed

stdlib/public/core/Filter.swift.gyb

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public func > <Base : Collection>(
179179
/// general operations on `LazyFilterCollection` instances may not have the
180180
/// documented complexity.
181181
public struct ${Self}<
182-
Base : Collection
182+
Base : ${collectionForTraversal(Traversal)}
183183
> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)} {
184184

185185
/// A type that represents a valid position in the collection.
@@ -236,43 +236,10 @@ public struct ${Self}<
236236
% if Traversal == 'Bidirectional':
237237
@warn_unused_result
238238
public func index(before i: Index) -> Index {
239-
fatalError("FIXME: swift-3-indexing-model: implement")
239+
return LazyFilterIndex(base: _previousFiltered(i.base))
240240
}
241241
% end
242242

243-
// TODO: swift-3-indexing-model - add docs
244-
@warn_unused_result
245-
public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
246-
_precondition(n >= 0,
247-
"Only BidirectionalCollections can be advanced by a negative amount")
248-
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
249-
250-
var index = i.base
251-
for _ in stride(from: 0, to: n, by: 1) {
252-
_nextFilteredInPlace(&index)
253-
}
254-
return LazyFilterIndex(base: index)
255-
}
256-
257-
// TODO: swift-3-indexing-model - add docs
258-
@warn_unused_result
259-
public func index(
260-
_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index
261-
) -> Index? {
262-
_precondition(n >= 0,
263-
"Only BidirectionalCollections can be advanced by a negative amount")
264-
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
265-
266-
var index = i.base
267-
for _ in stride(from: 0, to: n, by: 1) {
268-
if index == limit.base {
269-
return nil
270-
}
271-
_nextFilteredInPlace(&index)
272-
}
273-
return LazyFilterIndex(base: index)
274-
}
275-
276243
/// Returns the next `index` of an element that `self._predicate` matches
277244
/// otherwise `self._base.endIndex`
278245
@inline(__always)
@@ -295,6 +262,30 @@ public struct ${Self}<
295262
} while index != _base.endIndex && !_predicate(_base[index])
296263
}
297264

265+
% if Traversal == 'Bidirectional':
266+
/// Returns the previous `index` of an element that `self._predicate` matches
267+
/// otherwise `self._base.startIndex`
268+
@inline(__always)
269+
internal func _previousFiltered(_ index: Base.Index) -> Base.Index {
270+
var index = index
271+
_previousFilteredInPlace(&index)
272+
return index
273+
}
274+
275+
/// Returns the first index `i` preceding `index` where
276+
/// `_predicate(base[i]) == true`.
277+
///
278+
/// - Precondition: `index != startIndex`
279+
/// - Postcondition: `i < index`
280+
@inline(__always)
281+
internal func _previousFilteredInPlace(_ index: inout Base.Index) {
282+
_precondition(index != _base.startIndex, "can't retreat before startIndex")
283+
repeat {
284+
_base.formIndex(before: &index)
285+
} while !_predicate(_base[index])
286+
}
287+
% end
288+
298289
/// Access the element at `position`.
299290
///
300291
/// - Precondition: `position` is a valid position in `self` and
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// -*- swift -*-
2+
3+
// RUN: rm -rf %t
4+
// RUN: mkdir -p %t
5+
// RUN: %S/../../../utils/gyb %s -o %t/main.swift
6+
// RUN: %S/../../../utils/line-directive %t/main.swift -- %target-build-swift %t/main.swift -o %t/LazyFilterCollection.swift.a.out
7+
// RUN: %S/../../../utils/line-directive %t/main.swift -- %target-run %t/LazyFilterCollection.swift.a.out
8+
// REQUIRES: executable_test
9+
10+
import StdlibUnittest
11+
import StdlibCollectionUnittest
12+
13+
var CollectionTests = TestSuite("Collection")
14+
15+
%{
16+
variations = [('', 'Sequence'), ('', 'Collection'), ('Bidirectional', 'Collection')]
17+
}%
18+
19+
// Test collections using value types as elements.
20+
% for (traversal, kind) in variations:
21+
CollectionTests.add${traversal}${kind}Tests(
22+
make${kind}: { (elements: [OpaqueValue<Int>]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<OpaqueValue<Int>>> in
23+
// FIXME: create a better sequence and filter
24+
Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
25+
},
26+
wrapValue: identity,
27+
extractValue: identity,
28+
make${kind}OfEquatable: { (elements: [MinimalEquatableValue]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<MinimalEquatableValue>> in
29+
// FIXME: create a better sequence and filter
30+
Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
31+
},
32+
wrapValueIntoEquatable: identityEq,
33+
extractValueFromEquatable: identityEq
34+
)
35+
% end
36+
37+
// Test collections using reference types as elements.
38+
% for (traversal, kind) in variations:
39+
CollectionTests.add${traversal}${kind}Tests(
40+
make${kind}: { (elements: [LifetimeTracked]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<LifetimeTracked>> in
41+
// FIXME: create a better sequence and filter
42+
Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
43+
},
44+
wrapValue: { (element: OpaqueValue<Int>) in
45+
LifetimeTracked(element.value, identity: element.identity)
46+
},
47+
extractValue: { (element: LifetimeTracked) in
48+
OpaqueValue(element.value, identity: element.identity)
49+
},
50+
make${kind}OfEquatable: { (elements: [LifetimeTracked]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<LifetimeTracked>> in
51+
// FIXME: create a better sequence and filter
52+
Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
53+
},
54+
wrapValueIntoEquatable: { (element: MinimalEquatableValue) in
55+
LifetimeTracked(element.value, identity: element.identity)
56+
},
57+
extractValueFromEquatable: { (element: LifetimeTracked) in
58+
MinimalEquatableValue(element.value, identity: element.identity)
59+
}
60+
)
61+
% end
62+
63+
// Test collection instances and iterators.
64+
% for (traversal, kind) in variations:
65+
CollectionTests.test("LazyFilterCollection instances (${traversal}${kind})") {
66+
do {
67+
// MinimalIterator will error if next() is called after exhaustion
68+
var resiliency = CollectionMisuseResiliencyChecks.all
69+
resiliency.callNextOnExhaustedGenerator = false
70+
71+
let expected : [String] = []
72+
let base = ["apple", "orange", "banana", "grapefruit", "lychee"]
73+
% if kind == 'Sequence':
74+
checkSequence(
75+
expected,
76+
MinimalSequence(elements: base).lazy.filter { _ in return false },
77+
resiliencyChecks: resiliency
78+
)
79+
% elif traversal == '' and kind == 'Collection':
80+
checkForwardCollection(
81+
expected,
82+
MinimalCollection(elements: base).lazy.filter { _ in return false },
83+
resiliencyChecks: resiliency,
84+
sameValue: { $0 == $1 }
85+
)
86+
% else:
87+
check${traversal}${kind}(
88+
expected,
89+
Minimal${traversal}${kind}(elements: base).lazy.filter { _ in return false },
90+
resiliencyChecks: resiliency,
91+
sameValue: { $0 == $1 }
92+
)
93+
% end
94+
}
95+
do {
96+
var resiliency = CollectionMisuseResiliencyChecks.all
97+
resiliency.callNextOnExhaustedGenerator = false
98+
99+
let expected = ["apple", "orange", "banana", "grapefruit", "lychee"]
100+
let base = ["apple", "orange", "banana", "grapefruit", "lychee"]
101+
% if kind == 'Sequence':
102+
checkSequence(
103+
expected,
104+
MinimalSequence(elements: base).lazy.filter { _ in return true },
105+
resiliencyChecks: resiliency
106+
)
107+
% elif traversal == '' and kind == 'Collection':
108+
checkForwardCollection(
109+
expected,
110+
MinimalCollection(elements: base).lazy.filter { _ in return true },
111+
resiliencyChecks: resiliency,
112+
sameValue: { $0 == $1 }
113+
)
114+
% else:
115+
check${traversal}${kind}(
116+
expected,
117+
Minimal${traversal}${kind}(elements: base).lazy.filter { _ in return true },
118+
resiliencyChecks: resiliency,
119+
sameValue: { $0 == $1 }
120+
)
121+
% end
122+
}
123+
do {
124+
var resiliency = CollectionMisuseResiliencyChecks.all
125+
resiliency.callNextOnExhaustedGenerator = false
126+
127+
let expected = [2, 4, 6, 8, 10, 12, 14, 16]
128+
let base = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
129+
% if kind == 'Sequence':
130+
checkSequence(
131+
expected,
132+
MinimalSequence(elements: base).lazy.filter { $0 % 2 == 0 },
133+
resiliencyChecks: resiliency
134+
)
135+
% elif traversal == '' and kind == 'Collection':
136+
checkForwardCollection(
137+
expected,
138+
MinimalCollection(elements: base).lazy.filter { $0 % 2 == 0 },
139+
resiliencyChecks: resiliency,
140+
sameValue: { $0 == $1 }
141+
)
142+
% else:
143+
check${traversal}${kind}(
144+
expected,
145+
Minimal${traversal}${kind}(elements: base).lazy.filter { $0 % 2 == 0 },
146+
resiliencyChecks: resiliency,
147+
sameValue: { $0 == $1 }
148+
)
149+
% end
150+
}
151+
}
152+
% end
153+
154+
runAllTests()

0 commit comments

Comments
 (0)