OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'core_matchers.dart'; | 5 import 'core_matchers.dart'; |
6 import 'description.dart'; | 6 import 'description.dart'; |
7 import 'interfaces.dart'; | 7 import 'interfaces.dart'; |
8 import 'util.dart'; | 8 import 'util.dart'; |
9 | 9 |
10 /// Returns a matcher which matches [Iterable]s in which all elements | 10 /// Returns a matcher which matches [Iterable]s in which all elements |
11 /// match the given [matcher]. | 11 /// match the given [matcher]. |
12 Matcher everyElement(matcher) => new _EveryElement(wrapMatcher(matcher)); | 12 Matcher everyElement(matcher) => new _EveryElement(wrapMatcher(matcher)); |
13 | 13 |
14 class _EveryElement extends _IterableMatcher { | 14 class _EveryElement extends _IterableMatcher { |
15 final Matcher _matcher; | 15 final Matcher _matcher; |
16 | 16 |
17 _EveryElement(Matcher this._matcher); | 17 _EveryElement(this._matcher); |
18 | 18 |
19 bool matches(item, Map matchState) { | 19 bool matches(item, Map matchState) { |
20 if (item is! Iterable) { | 20 if (item is! Iterable) { |
21 return false; | 21 return false; |
22 } | 22 } |
23 var i = 0; | 23 var i = 0; |
24 for (var element in item) { | 24 for (var element in item) { |
25 if (!_matcher.matches(element, matchState)) { | 25 if (!_matcher.matches(element, matchState)) { |
26 addStateInfo(matchState, {'index': i, 'element': element}); | 26 addStateInfo(matchState, {'index': i, 'element': element}); |
27 return false; | 27 return false; |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 /// Note that this is `O(n^2)` and so should only be used on small objects. | 148 /// Note that this is `O(n^2)` and so should only be used on small objects. |
149 Matcher unorderedMatches(Iterable expected) => new _UnorderedMatches(expected); | 149 Matcher unorderedMatches(Iterable expected) => new _UnorderedMatches(expected); |
150 | 150 |
151 class _UnorderedMatches extends Matcher { | 151 class _UnorderedMatches extends Matcher { |
152 final List<Matcher> _expected; | 152 final List<Matcher> _expected; |
153 | 153 |
154 _UnorderedMatches(Iterable expected) | 154 _UnorderedMatches(Iterable expected) |
155 : _expected = expected.map(wrapMatcher).toList(); | 155 : _expected = expected.map(wrapMatcher).toList(); |
156 | 156 |
157 String _test(item) { | 157 String _test(item) { |
158 if (item is! Iterable) return 'not iterable'; | 158 if (item is Iterable) { |
159 item = item.toList(); | 159 var list = item.toList(); |
160 | 160 |
161 // Check the lengths are the same. | 161 // Check the lengths are the same. |
162 if (_expected.length > item.length) { | 162 if (_expected.length > list.length) { |
163 return 'has too few elements (${item.length} < ${_expected.length})'; | 163 return 'has too few elements (${list.length} < ${_expected.length})'; |
164 } else if (_expected.length < item.length) { | 164 } else if (_expected.length < list.length) { |
165 return 'has too many elements (${item.length} > ${_expected.length})'; | 165 return 'has too many elements (${list.length} > ${_expected.length})'; |
166 } | |
167 | |
168 var matched = new List<bool>.filled(item.length, false); | |
169 var expectedPosition = 0; | |
170 for (var expectedMatcher in _expected) { | |
171 var actualPosition = 0; | |
172 var gotMatch = false; | |
173 for (var actualElement in item) { | |
174 if (!matched[actualPosition]) { | |
175 if (expectedMatcher.matches(actualElement, {})) { | |
176 matched[actualPosition] = gotMatch = true; | |
177 break; | |
178 } | |
179 } | |
180 ++actualPosition; | |
181 } | 166 } |
182 | 167 |
183 if (!gotMatch) { | 168 var matched = new List<bool>.filled(list.length, false); |
184 return new StringDescription() | 169 var expectedPosition = 0; |
185 .add('has no match for ') | 170 for (var expectedMatcher in _expected) { |
186 .addDescriptionOf(expectedMatcher) | 171 var actualPosition = 0; |
187 .add(' at index ${expectedPosition}') | 172 var gotMatch = false; |
188 .toString(); | 173 for (var actualElement in list) { |
| 174 if (!matched[actualPosition]) { |
| 175 if (expectedMatcher.matches(actualElement, {})) { |
| 176 matched[actualPosition] = gotMatch = true; |
| 177 break; |
| 178 } |
| 179 } |
| 180 ++actualPosition; |
| 181 } |
| 182 |
| 183 if (!gotMatch) { |
| 184 return new StringDescription() |
| 185 .add('has no match for ') |
| 186 .addDescriptionOf(expectedMatcher) |
| 187 .add(' at index $expectedPosition') |
| 188 .toString(); |
| 189 } |
| 190 |
| 191 ++expectedPosition; |
189 } | 192 } |
190 | 193 return null; |
191 ++expectedPosition; | 194 } else { |
| 195 return 'not iterable'; |
192 } | 196 } |
193 return null; | |
194 } | 197 } |
195 | 198 |
196 bool matches(item, Map mismatchState) => _test(item) == null; | 199 bool matches(item, Map mismatchState) => _test(item) == null; |
197 | 200 |
198 Description describe(Description description) => description | 201 Description describe(Description description) => description |
199 .add('matches ') | 202 .add('matches ') |
200 .addAll('[', ', ', ']', _expected) | 203 .addAll('[', ', ', ']', _expected) |
201 .add(' unordered'); | 204 .add(' unordered'); |
202 | 205 |
203 Description describeMismatch(item, Description mismatchDescription, | 206 Description describeMismatch(item, Description mismatchDescription, |
204 Map matchState, bool verbose) => | 207 Map matchState, bool verbose) => |
205 mismatchDescription.add(_test(item)); | 208 mismatchDescription.add(_test(item)); |
206 } | 209 } |
207 | 210 |
208 /// A pairwise matcher for [Iterable]s. | 211 /// A pairwise matcher for [Iterable]s. |
209 /// | 212 /// |
210 /// The [comparator] function, taking an expected and an actual argument, and | 213 /// The [comparator] function, taking an expected and an actual argument, and |
211 /// returning whether they match, will be applied to each pair in order. | 214 /// returning whether they match, will be applied to each pair in order. |
212 /// [description] should be a meaningful name for the comparator. | 215 /// [description] should be a meaningful name for the comparator. |
213 Matcher pairwiseCompare( | 216 Matcher pairwiseCompare<S, T>( |
214 Iterable expected, bool comparator(a, b), String description) => | 217 Iterable<S> expected, bool comparator(S a, T b), String description) => |
215 new _PairwiseCompare(expected, comparator, description); | 218 new _PairwiseCompare(expected, comparator, description); |
216 | 219 |
217 typedef bool _Comparator(a, b); | 220 typedef bool _Comparator<S, T>(S a, T b); |
218 | 221 |
219 class _PairwiseCompare extends _IterableMatcher { | 222 class _PairwiseCompare<S, T> extends _IterableMatcher { |
220 final Iterable _expected; | 223 final Iterable<S> _expected; |
221 final _Comparator _comparator; | 224 final _Comparator<S, T> _comparator; |
222 final String _description; | 225 final String _description; |
223 | 226 |
224 _PairwiseCompare(this._expected, this._comparator, this._description); | 227 _PairwiseCompare(this._expected, this._comparator, this._description); |
225 | 228 |
226 bool matches(item, Map matchState) { | 229 bool matches(item, Map matchState) { |
227 if (item is! Iterable) return false; | 230 if (item is Iterable) { |
228 if (item.length != _expected.length) return false; | 231 if (item.length != _expected.length) return false; |
229 var iterator = item.iterator; | 232 var iterator = item.iterator; |
230 var i = 0; | 233 var i = 0; |
231 for (var e in _expected) { | 234 for (var e in _expected) { |
232 iterator.moveNext(); | 235 iterator.moveNext(); |
233 if (!_comparator(e, iterator.current)) { | 236 if (!_comparator(e, iterator.current)) { |
234 addStateInfo(matchState, | 237 addStateInfo(matchState, |
235 {'index': i, 'expected': e, 'actual': iterator.current}); | 238 {'index': i, 'expected': e, 'actual': iterator.current}); |
236 return false; | 239 return false; |
| 240 } |
| 241 i++; |
237 } | 242 } |
238 i++; | 243 return true; |
| 244 } else { |
| 245 return false; |
239 } | 246 } |
240 return true; | |
241 } | 247 } |
242 | 248 |
243 Description describe(Description description) => | 249 Description describe(Description description) => |
244 description.add('pairwise $_description ').addDescriptionOf(_expected); | 250 description.add('pairwise $_description ').addDescriptionOf(_expected); |
245 | 251 |
246 Description describeMismatch( | 252 Description describeMismatch( |
247 item, Description mismatchDescription, Map matchState, bool verbose) { | 253 item, Description mismatchDescription, Map matchState, bool verbose) { |
248 if (item is! Iterable) { | 254 if (item is! Iterable) { |
249 return mismatchDescription.add('is not an Iterable'); | 255 return mismatchDescription.add('is not an Iterable'); |
250 } else if (item.length != _expected.length) { | 256 } else if (item.length != _expected.length) { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
297 Description describe(Description description) => description | 303 Description describe(Description description) => description |
298 .add('contains in order(') | 304 .add('contains in order(') |
299 .addDescriptionOf(_expected) | 305 .addDescriptionOf(_expected) |
300 .add(')'); | 306 .add(')'); |
301 | 307 |
302 @override | 308 @override |
303 Description describeMismatch(item, Description mismatchDescription, | 309 Description describeMismatch(item, Description mismatchDescription, |
304 Map matchState, bool verbose) => | 310 Map matchState, bool verbose) => |
305 mismatchDescription.add(_test(item, matchState)); | 311 mismatchDescription.add(_test(item, matchState)); |
306 } | 312 } |
OLD | NEW |