| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of unittest.matcher; | |
| 6 | |
| 7 /** | |
| 8 * Returns a matcher which matches [Iterable]s in which all elements | |
| 9 * match the given [matcher]. | |
| 10 */ | |
| 11 Matcher everyElement(matcher) => new _EveryElement(wrapMatcher(matcher)); | |
| 12 | |
| 13 class _EveryElement extends _IterableMatcher { | |
| 14 Matcher _matcher; | |
| 15 | |
| 16 _EveryElement(Matcher this._matcher); | |
| 17 | |
| 18 bool matches(item, Map matchState) { | |
| 19 if (item is! Iterable) { | |
| 20 return false; | |
| 21 } | |
| 22 var i = 0; | |
| 23 for (var element in item) { | |
| 24 if (!_matcher.matches(element, matchState)) { | |
| 25 addStateInfo(matchState, {'index': i, 'element': element}); | |
| 26 return false; | |
| 27 } | |
| 28 ++i; | |
| 29 } | |
| 30 return true; | |
| 31 } | |
| 32 | |
| 33 Description describe(Description description) => | |
| 34 description.add('every element(').addDescriptionOf(_matcher).add(')'); | |
| 35 | |
| 36 Description describeMismatch(item, Description mismatchDescription, | |
| 37 Map matchState, bool verbose) { | |
| 38 if (matchState['index'] != null) { | |
| 39 var index = matchState['index']; | |
| 40 var element = matchState['element']; | |
| 41 mismatchDescription.add('has value ').addDescriptionOf(element). | |
| 42 add(' which '); | |
| 43 var subDescription = new StringDescription(); | |
| 44 _matcher.describeMismatch(element, subDescription, | |
| 45 matchState['state'], verbose); | |
| 46 if (subDescription.length > 0) { | |
| 47 mismatchDescription.add(subDescription); | |
| 48 } else { | |
| 49 mismatchDescription.add("doesn't match "); | |
| 50 _matcher.describe(mismatchDescription); | |
| 51 } | |
| 52 mismatchDescription.add(' at index $index'); | |
| 53 return mismatchDescription; | |
| 54 } | |
| 55 return super.describeMismatch(item, mismatchDescription, | |
| 56 matchState, verbose); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 /** | |
| 61 * Returns a matcher which matches [Iterable]s in which at least one | |
| 62 * element matches the given [matcher]. | |
| 63 */ | |
| 64 Matcher anyElement(matcher) => new _AnyElement(wrapMatcher(matcher)); | |
| 65 | |
| 66 class _AnyElement extends _IterableMatcher { | |
| 67 Matcher _matcher; | |
| 68 | |
| 69 _AnyElement(this._matcher); | |
| 70 | |
| 71 bool matches(item, Map matchState) { | |
| 72 return item.any((e) => _matcher.matches(e, matchState)); | |
| 73 } | |
| 74 | |
| 75 Description describe(Description description) => | |
| 76 description.add('some element ').addDescriptionOf(_matcher); | |
| 77 } | |
| 78 | |
| 79 /** | |
| 80 * Returns a matcher which matches [Iterable]s that have the same | |
| 81 * length and the same elements as [expected], and in the same order. | |
| 82 * This is equivalent to equals but does not recurse. | |
| 83 */ | |
| 84 | |
| 85 Matcher orderedEquals(Iterable expected) => new _OrderedEquals(expected); | |
| 86 | |
| 87 class _OrderedEquals extends Matcher { | |
| 88 final Iterable _expected; | |
| 89 Matcher _matcher; | |
| 90 | |
| 91 _OrderedEquals(this._expected) { | |
| 92 _matcher = equals(_expected, 1); | |
| 93 } | |
| 94 | |
| 95 bool matches(item, Map matchState) => | |
| 96 (item is Iterable) && _matcher.matches(item, matchState); | |
| 97 | |
| 98 Description describe(Description description) => | |
| 99 description.add('equals ').addDescriptionOf(_expected).add(' ordered'); | |
| 100 | |
| 101 Description describeMismatch(item, Description mismatchDescription, | |
| 102 Map matchState, bool verbose) { | |
| 103 if (item is !Iterable) { | |
| 104 return mismatchDescription.add('is not an Iterable'); | |
| 105 } else { | |
| 106 return _matcher.describeMismatch(item, mismatchDescription, | |
| 107 matchState, verbose); | |
| 108 } | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 /** | |
| 113 * Returns a matcher which matches [Iterable]s that have the same | |
| 114 * length and the same elements as [expected], but not necessarily in | |
| 115 * the same order. Note that this is O(n^2) so should only be used on | |
| 116 * small objects. | |
| 117 */ | |
| 118 Matcher unorderedEquals(Iterable expected) => new _UnorderedEquals(expected); | |
| 119 | |
| 120 class _UnorderedEquals extends _UnorderedMatches { | |
| 121 final List _expectedValues; | |
| 122 | |
| 123 _UnorderedEquals(Iterable expected) | |
| 124 : super(expected.map(equals)), | |
| 125 _expectedValues = expected.toList(); | |
| 126 | |
| 127 Description describe(Description description) => | |
| 128 description | |
| 129 .add('equals ') | |
| 130 .addDescriptionOf(_expectedValues) | |
| 131 .add(' unordered'); | |
| 132 } | |
| 133 | |
| 134 /** | |
| 135 * Iterable matchers match against [Iterable]s. We add this intermediate | |
| 136 * class to give better mismatch error messages than the base Matcher class. | |
| 137 */ | |
| 138 abstract class _IterableMatcher extends Matcher { | |
| 139 const _IterableMatcher(); | |
| 140 Description describeMismatch(item, Description mismatchDescription, | |
| 141 Map matchState, bool verbose) { | |
| 142 if (item is! Iterable) { | |
| 143 return mismatchDescription. | |
| 144 addDescriptionOf(item). | |
| 145 add(' not an Iterable'); | |
| 146 } else { | |
| 147 return super.describeMismatch(item, mismatchDescription, matchState, | |
| 148 verbose); | |
| 149 } | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 /** | |
| 154 * Returns a matcher which matches [Iterable]s whose elements match the matchers | |
| 155 * in [expected], but not necessarily in the same order. | |
| 156 * | |
| 157 * Note that this is `O(n^2)` and so should only be used on small objects. | |
| 158 */ | |
| 159 Matcher unorderedMatches(Iterable expected) => new _UnorderedMatches(expected); | |
| 160 | |
| 161 class _UnorderedMatches extends Matcher { | |
| 162 final List<Matcher> _expected; | |
| 163 | |
| 164 _UnorderedMatches(Iterable expected) | |
| 165 : _expected = expected.map(wrapMatcher).toList(); | |
| 166 | |
| 167 String _test(item) { | |
| 168 if (item is! Iterable) return 'not iterable'; | |
| 169 item = item.toList(); | |
| 170 | |
| 171 // Check the lengths are the same. | |
| 172 if (_expected.length > item.length) { | |
| 173 return 'has too few elements (${item.length} < ${_expected.length})'; | |
| 174 } else if (_expected.length < item.length) { | |
| 175 return 'has too many elements (${item.length} > ${_expected.length})'; | |
| 176 } | |
| 177 | |
| 178 var matched = new List<bool>.filled(item.length, false); | |
| 179 var expectedPosition = 0; | |
| 180 for (var expectedMatcher in _expected) { | |
| 181 var actualPosition = 0; | |
| 182 var gotMatch = false; | |
| 183 for (var actualElement in item) { | |
| 184 if (!matched[actualPosition]) { | |
| 185 if (expectedMatcher.matches(actualElement, {})) { | |
| 186 matched[actualPosition] = gotMatch = true; | |
| 187 break; | |
| 188 } | |
| 189 } | |
| 190 ++actualPosition; | |
| 191 } | |
| 192 | |
| 193 if (!gotMatch) { | |
| 194 return new StringDescription() | |
| 195 .add('has no match for ') | |
| 196 .addDescriptionOf(expectedMatcher) | |
| 197 .add(' at index ${expectedPosition}') | |
| 198 .toString(); | |
| 199 } | |
| 200 | |
| 201 ++expectedPosition; | |
| 202 } | |
| 203 return null; | |
| 204 } | |
| 205 | |
| 206 bool matches(item, Map mismatchState) => _test(item) == null; | |
| 207 | |
| 208 Description describe(Description description) => | |
| 209 description | |
| 210 .add('matches ') | |
| 211 .addAll('[', ', ', ']', _expected) | |
| 212 .add(' unordered'); | |
| 213 | |
| 214 Description describeMismatch(item, Description mismatchDescription, | |
| 215 Map matchState, bool verbose) => | |
| 216 mismatchDescription.add(_test(item)); | |
| 217 } | |
| 218 | |
| 219 /** | |
| 220 * A pairwise matcher for iterable. You can pass an arbitrary [comparator] | |
| 221 * function that takes an expected and actual argument which will be applied | |
| 222 * to each pair in order. [description] should be a meaningful name for | |
| 223 * the comparator. | |
| 224 */ | |
| 225 Matcher pairwiseCompare(Iterable expected, Function comparator, | |
| 226 String description) => | |
| 227 new _PairwiseCompare(expected, comparator, description); | |
| 228 | |
| 229 class _PairwiseCompare extends _IterableMatcher { | |
| 230 Iterable _expected; | |
| 231 Function _comparator; | |
| 232 String _description; | |
| 233 | |
| 234 _PairwiseCompare(this._expected, this._comparator, this._description); | |
| 235 | |
| 236 bool matches(item, Map matchState) { | |
| 237 if (item is! Iterable) return false; | |
| 238 if (item.length != _expected.length) return false; | |
| 239 var iterator = item.iterator; | |
| 240 var i = 0; | |
| 241 for (var e in _expected) { | |
| 242 iterator.moveNext(); | |
| 243 if (!_comparator(e, iterator.current)) { | |
| 244 addStateInfo(matchState, {'index': i, 'expected': e, | |
| 245 'actual': iterator.current}); | |
| 246 return false; | |
| 247 } | |
| 248 i++; | |
| 249 } | |
| 250 return true; | |
| 251 } | |
| 252 | |
| 253 Description describe(Description description) => | |
| 254 description.add('pairwise $_description ').addDescriptionOf(_expected); | |
| 255 | |
| 256 Description describeMismatch(item, Description mismatchDescription, | |
| 257 Map matchState, bool verbose) { | |
| 258 if (item is !Iterable) { | |
| 259 return mismatchDescription.add('is not an Iterable'); | |
| 260 } else if (item.length != _expected.length) { | |
| 261 return mismatchDescription. | |
| 262 add('has length ${item.length} instead of ${_expected.length}'); | |
| 263 } else { | |
| 264 return mismatchDescription. | |
| 265 add('has '). | |
| 266 addDescriptionOf(matchState["actual"]). | |
| 267 add(' which is not $_description '). | |
| 268 addDescriptionOf(matchState["expected"]). | |
| 269 add(' at index ${matchState["index"]}'); | |
| 270 } | |
| 271 } | |
| 272 } | |
| 273 | |
| OLD | NEW |