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 library matcher.iterable_matchers; | 5 library matcher.iterable_matchers; |
6 | 6 |
7 import 'core_matchers.dart'; | 7 import 'core_matchers.dart'; |
8 import 'description.dart'; | 8 import 'description.dart'; |
9 import 'interfaces.dart'; | 9 import 'interfaces.dart'; |
10 import 'util.dart'; | 10 import 'util.dart'; |
(...skipping 18 matching lines...) Expand all Loading... |
29 return false; | 29 return false; |
30 } | 30 } |
31 ++i; | 31 ++i; |
32 } | 32 } |
33 return true; | 33 return true; |
34 } | 34 } |
35 | 35 |
36 Description describe(Description description) => | 36 Description describe(Description description) => |
37 description.add('every element(').addDescriptionOf(_matcher).add(')'); | 37 description.add('every element(').addDescriptionOf(_matcher).add(')'); |
38 | 38 |
39 Description describeMismatch(item, Description mismatchDescription, | 39 Description describeMismatch( |
40 Map matchState, bool verbose) { | 40 item, Description mismatchDescription, Map matchState, bool verbose) { |
41 if (matchState['index'] != null) { | 41 if (matchState['index'] != null) { |
42 var index = matchState['index']; | 42 var index = matchState['index']; |
43 var element = matchState['element']; | 43 var element = matchState['element']; |
44 mismatchDescription.add('has value ').addDescriptionOf(element). | 44 mismatchDescription |
45 add(' which '); | 45 .add('has value ') |
| 46 .addDescriptionOf(element) |
| 47 .add(' which '); |
46 var subDescription = new StringDescription(); | 48 var subDescription = new StringDescription(); |
47 _matcher.describeMismatch(element, subDescription, matchState['state'], | 49 _matcher.describeMismatch( |
48 verbose); | 50 element, subDescription, matchState['state'], verbose); |
49 if (subDescription.length > 0) { | 51 if (subDescription.length > 0) { |
50 mismatchDescription.add(subDescription.toString()); | 52 mismatchDescription.add(subDescription.toString()); |
51 } else { | 53 } else { |
52 mismatchDescription.add("doesn't match "); | 54 mismatchDescription.add("doesn't match "); |
53 _matcher.describe(mismatchDescription); | 55 _matcher.describe(mismatchDescription); |
54 } | 56 } |
55 mismatchDescription.add(' at index $index'); | 57 mismatchDescription.add(' at index $index'); |
56 return mismatchDescription; | 58 return mismatchDescription; |
57 } | 59 } |
58 return super.describeMismatch(item, mismatchDescription, | 60 return super.describeMismatch( |
59 matchState, verbose); | 61 item, mismatchDescription, matchState, verbose); |
60 } | 62 } |
61 } | 63 } |
62 | 64 |
63 /// Returns a matcher which matches [Iterable]s in which at least one | 65 /// Returns a matcher which matches [Iterable]s in which at least one |
64 /// element matches the given [matcher]. | 66 /// element matches the given [matcher]. |
65 Matcher anyElement(matcher) => new _AnyElement(wrapMatcher(matcher)); | 67 Matcher anyElement(matcher) => new _AnyElement(wrapMatcher(matcher)); |
66 | 68 |
67 class _AnyElement extends _IterableMatcher { | 69 class _AnyElement extends _IterableMatcher { |
68 final Matcher _matcher; | 70 final Matcher _matcher; |
69 | 71 |
(...skipping 20 matching lines...) Expand all Loading... |
90 _OrderedEquals(this._expected) { | 92 _OrderedEquals(this._expected) { |
91 _matcher = equals(_expected, 1); | 93 _matcher = equals(_expected, 1); |
92 } | 94 } |
93 | 95 |
94 bool matches(item, Map matchState) => | 96 bool matches(item, Map matchState) => |
95 (item is Iterable) && _matcher.matches(item, matchState); | 97 (item is Iterable) && _matcher.matches(item, matchState); |
96 | 98 |
97 Description describe(Description description) => | 99 Description describe(Description description) => |
98 description.add('equals ').addDescriptionOf(_expected).add(' ordered'); | 100 description.add('equals ').addDescriptionOf(_expected).add(' ordered'); |
99 | 101 |
100 Description describeMismatch(item, Description mismatchDescription, | 102 Description describeMismatch( |
101 Map matchState, bool verbose) { | 103 item, Description mismatchDescription, Map matchState, bool verbose) { |
102 if (item is !Iterable) { | 104 if (item is! Iterable) { |
103 return mismatchDescription.add('is not an Iterable'); | 105 return mismatchDescription.add('is not an Iterable'); |
104 } else { | 106 } else { |
105 return _matcher.describeMismatch(item, mismatchDescription, | 107 return _matcher.describeMismatch( |
106 matchState, verbose); | 108 item, mismatchDescription, matchState, verbose); |
107 } | 109 } |
108 } | 110 } |
109 } | 111 } |
110 | 112 |
111 /// Returns a matcher which matches [Iterable]s that have the same length and | 113 /// Returns a matcher which matches [Iterable]s that have the same length and |
112 /// the same elements as [expected], but not necessarily in the same order. | 114 /// the same elements as [expected], but not necessarily in the same order. |
113 /// | 115 /// |
114 /// Note that this is O(n^2) so should only be used on small objects. | 116 /// Note that this is O(n^2) so should only be used on small objects. |
115 Matcher unorderedEquals(Iterable expected) => new _UnorderedEquals(expected); | 117 Matcher unorderedEquals(Iterable expected) => new _UnorderedEquals(expected); |
116 | 118 |
117 class _UnorderedEquals extends _UnorderedMatches { | 119 class _UnorderedEquals extends _UnorderedMatches { |
118 final List _expectedValues; | 120 final List _expectedValues; |
119 | 121 |
120 _UnorderedEquals(Iterable expected) | 122 _UnorderedEquals(Iterable expected) |
121 : super(expected.map(equals)), | 123 : super(expected.map(equals)), |
122 _expectedValues = expected.toList(); | 124 _expectedValues = expected.toList(); |
123 | 125 |
124 Description describe(Description description) => | 126 Description describe(Description description) => description |
125 description | 127 .add('equals ') |
126 .add('equals ') | 128 .addDescriptionOf(_expectedValues) |
127 .addDescriptionOf(_expectedValues) | 129 .add(' unordered'); |
128 .add(' unordered'); | |
129 } | 130 } |
130 | 131 |
131 /// Iterable matchers match against [Iterable]s. We add this intermediate | 132 /// Iterable matchers match against [Iterable]s. We add this intermediate |
132 /// class to give better mismatch error messages than the base Matcher class. | 133 /// class to give better mismatch error messages than the base Matcher class. |
133 abstract class _IterableMatcher extends Matcher { | 134 abstract class _IterableMatcher extends Matcher { |
134 const _IterableMatcher(); | 135 const _IterableMatcher(); |
135 Description describeMismatch(item, Description mismatchDescription, | 136 Description describeMismatch( |
136 Map matchState, bool verbose) { | 137 item, Description mismatchDescription, Map matchState, bool verbose) { |
137 if (item is! Iterable) { | 138 if (item is! Iterable) { |
138 return mismatchDescription. | 139 return mismatchDescription.addDescriptionOf(item).add(' not an Iterable'); |
139 addDescriptionOf(item). | |
140 add(' not an Iterable'); | |
141 } else { | 140 } else { |
142 return super.describeMismatch(item, mismatchDescription, matchState, | 141 return super.describeMismatch( |
143 verbose); | 142 item, mismatchDescription, matchState, verbose); |
144 } | 143 } |
145 } | 144 } |
146 } | 145 } |
147 | 146 |
148 /// Returns a matcher which matches [Iterable]s whose elements match the | 147 /// Returns a matcher which matches [Iterable]s whose elements match the |
149 /// matchers in [expected], but not necessarily in the same order. | 148 /// matchers in [expected], but not necessarily in the same order. |
150 /// | 149 /// |
151 /// Note that this is `O(n^2)` and so should only be used on small objects. | 150 /// Note that this is `O(n^2)` and so should only be used on small objects. |
152 Matcher unorderedMatches(Iterable expected) => new _UnorderedMatches(expected); | 151 Matcher unorderedMatches(Iterable expected) => new _UnorderedMatches(expected); |
153 | 152 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
191 .toString(); | 190 .toString(); |
192 } | 191 } |
193 | 192 |
194 ++expectedPosition; | 193 ++expectedPosition; |
195 } | 194 } |
196 return null; | 195 return null; |
197 } | 196 } |
198 | 197 |
199 bool matches(item, Map mismatchState) => _test(item) == null; | 198 bool matches(item, Map mismatchState) => _test(item) == null; |
200 | 199 |
201 Description describe(Description description) => | 200 Description describe(Description description) => description |
202 description | |
203 .add('matches ') | 201 .add('matches ') |
204 .addAll('[', ', ', ']', _expected) | 202 .addAll('[', ', ', ']', _expected) |
205 .add(' unordered'); | 203 .add(' unordered'); |
206 | 204 |
207 Description describeMismatch(item, Description mismatchDescription, | 205 Description describeMismatch( |
208 Map matchState, bool verbose) => | 206 item, Description mismatchDescription, Map matchState, bool verbose) => |
209 mismatchDescription.add(_test(item)); | 207 mismatchDescription.add(_test(item)); |
210 } | 208 } |
211 | 209 |
212 /// A pairwise matcher for [Iterable]s. | 210 /// A pairwise matcher for [Iterable]s. |
213 /// | 211 /// |
214 /// The [comparator] function, taking an expected and an actual argument, and | 212 /// The [comparator] function, taking an expected and an actual argument, and |
215 /// returning whether they match, will be applied to each pair in order. | 213 /// returning whether they match, will be applied to each pair in order. |
216 /// [description] should be a meaningful name for the comparator. | 214 /// [description] should be a meaningful name for the comparator. |
217 Matcher pairwiseCompare(Iterable expected, bool comparator(a, b), | 215 Matcher pairwiseCompare( |
218 String description) => | 216 Iterable expected, bool comparator(a, b), String description) => |
219 new _PairwiseCompare(expected, comparator, description); | 217 new _PairwiseCompare(expected, comparator, description); |
220 | 218 |
221 typedef bool _Comparator(a, b); | 219 typedef bool _Comparator(a, b); |
222 | 220 |
223 class _PairwiseCompare extends _IterableMatcher { | 221 class _PairwiseCompare extends _IterableMatcher { |
224 final Iterable _expected; | 222 final Iterable _expected; |
225 final _Comparator _comparator; | 223 final _Comparator _comparator; |
226 final String _description; | 224 final String _description; |
227 | 225 |
228 _PairwiseCompare(this._expected, this._comparator, this._description); | 226 _PairwiseCompare(this._expected, this._comparator, this._description); |
229 | 227 |
230 bool matches(item, Map matchState) { | 228 bool matches(item, Map matchState) { |
231 if (item is! Iterable) return false; | 229 if (item is! Iterable) return false; |
232 if (item.length != _expected.length) return false; | 230 if (item.length != _expected.length) return false; |
233 var iterator = item.iterator; | 231 var iterator = item.iterator; |
234 var i = 0; | 232 var i = 0; |
235 for (var e in _expected) { | 233 for (var e in _expected) { |
236 iterator.moveNext(); | 234 iterator.moveNext(); |
237 if (!_comparator(e, iterator.current)) { | 235 if (!_comparator(e, iterator.current)) { |
238 addStateInfo(matchState, {'index': i, 'expected': e, | 236 addStateInfo(matchState, { |
239 'actual': iterator.current}); | 237 'index': i, |
| 238 'expected': e, |
| 239 'actual': iterator.current |
| 240 }); |
240 return false; | 241 return false; |
241 } | 242 } |
242 i++; | 243 i++; |
243 } | 244 } |
244 return true; | 245 return true; |
245 } | 246 } |
246 | 247 |
247 Description describe(Description description) => | 248 Description describe(Description description) => |
248 description.add('pairwise $_description ').addDescriptionOf(_expected); | 249 description.add('pairwise $_description ').addDescriptionOf(_expected); |
249 | 250 |
250 Description describeMismatch(item, Description mismatchDescription, | 251 Description describeMismatch( |
251 Map matchState, bool verbose) { | 252 item, Description mismatchDescription, Map matchState, bool verbose) { |
252 if (item is !Iterable) { | 253 if (item is! Iterable) { |
253 return mismatchDescription.add('is not an Iterable'); | 254 return mismatchDescription.add('is not an Iterable'); |
254 } else if (item.length != _expected.length) { | 255 } else if (item.length != _expected.length) { |
255 return mismatchDescription. | 256 return mismatchDescription |
256 add('has length ${item.length} instead of ${_expected.length}'); | 257 .add('has length ${item.length} instead of ${_expected.length}'); |
257 } else { | 258 } else { |
258 return mismatchDescription. | 259 return mismatchDescription |
259 add('has '). | 260 .add('has ') |
260 addDescriptionOf(matchState["actual"]). | 261 .addDescriptionOf(matchState["actual"]) |
261 add(' which is not $_description '). | 262 .add(' which is not $_description ') |
262 addDescriptionOf(matchState["expected"]). | 263 .addDescriptionOf(matchState["expected"]) |
263 add(' at index ${matchState["index"]}'); | 264 .add(' at index ${matchState["index"]}'); |
264 } | 265 } |
265 } | 266 } |
266 } | 267 } |
OLD | NEW |