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 /** | |
6 * Returns a matcher which matches [Collection]s in which all elements | |
7 * match the given [matcher]. | |
8 */ | |
9 Matcher everyElement(matcher) => new _EveryElement(wrapMatcher(matcher)); | |
10 | |
11 class _EveryElement extends _CollectionMatcher { | |
12 Matcher _matcher; | |
13 | |
14 _EveryElement(Matcher this._matcher); | |
15 | |
16 bool matches(item, MatchState matchState) { | |
17 if (item is! Iterable) { | |
18 return false; | |
19 } | |
20 var i = 0; | |
21 for (var element in item) { | |
22 if (!_matcher.matches(element, matchState)) { | |
23 matchState.state = { | |
24 'index': i, | |
25 'element': element, | |
26 'state': matchState.state | |
27 }; | |
28 return false; | |
29 } | |
30 ++i; | |
31 } | |
32 return true; | |
33 } | |
34 | |
35 Description describe(Description description) => | |
36 description.add('every element ').addDescriptionOf(_matcher); | |
37 | |
38 Description describeMismatch(item, Description mismatchDescription, | |
39 MatchState matchState, bool verbose) { | |
40 if (matchState.state != null) { | |
41 var index = matchState.state['index']; | |
42 var element = matchState.state['element']; | |
43 return _matcher.describeMismatch(element, mismatchDescription, | |
44 matchState.state['state'], verbose).add(' at position $index'); | |
45 } | |
46 return super.describeMismatch(item, mismatchDescription, | |
47 matchState, verbose); | |
48 } | |
49 } | |
50 | |
51 /** | |
52 * Returns a matcher which matches [Collection]s in which at least one | |
53 * element matches the given [matcher]. | |
54 */ | |
55 Matcher someElement(matcher) => new _SomeElement(wrapMatcher(matcher)); | |
56 | |
57 class _SomeElement extends _CollectionMatcher { | |
58 Matcher _matcher; | |
59 | |
60 _SomeElement(this._matcher); | |
61 | |
62 bool matches(item, MatchState matchState) { | |
63 return item.some( (e) => _matcher.matches(e, matchState) ); | |
64 } | |
65 | |
66 Description describe(Description description) => | |
67 description.add('some element ').addDescriptionOf(_matcher); | |
68 } | |
69 | |
70 /** | |
71 * Returns a matcher which matches [Iterable]s that have the same | |
72 * length and the same elements as [expected], and in the same order. | |
73 * This is equivalent to equals but does not recurse. | |
74 */ | |
75 | |
76 Matcher orderedEquals(Iterable expected) => new _OrderedEquals(expected); | |
77 | |
78 class _OrderedEquals extends BaseMatcher { | |
79 final Iterable _expected; | |
80 Matcher _matcher; | |
81 | |
82 _OrderedEquals(this._expected) { | |
83 _matcher = equals(_expected, 1); | |
84 } | |
85 | |
86 bool matches(item, MatchState matchState) => | |
87 (item is Iterable) && _matcher.matches(item, matchState); | |
88 | |
89 Description describe(Description description) => | |
90 description.add('equals ').addDescriptionOf(_expected).add(' ordered'); | |
91 | |
92 Description describeMismatch(item, Description mismatchDescription, | |
93 MatchState matchState, bool verbose) { | |
94 if (item is !Iterable) { | |
95 return mismatchDescription.add('not an Iterable'); | |
96 } else { | |
97 return _matcher.describeMismatch(item, mismatchDescription, | |
98 matchState, verbose); | |
99 } | |
100 } | |
101 } | |
102 /** | |
103 * Returns a matcher which matches [Iterable]s that have the same | |
104 * length and the same elements as [expected], but not necessarily in | |
105 * the same order. Note that this is O(n^2) so should only be used on | |
106 * small objects. | |
107 */ | |
108 Matcher unorderedEquals(Iterable expected) => | |
109 new _UnorderedEquals(expected); | |
110 | |
111 class _UnorderedEquals extends BaseMatcher { | |
112 Iterable _expected; | |
113 | |
114 _UnorderedEquals(Iterable this._expected); | |
115 | |
116 String _test(item) { | |
117 if (item is !Iterable) { | |
118 return 'not iterable'; | |
119 } | |
120 // Check the lengths are the same. | |
121 var expectedLength = 0; | |
122 if (_expected is Collection) { | |
123 Collection cast = _expected; // "_expected as Collection" | |
124 expectedLength = cast.length; | |
125 } else { | |
126 for (var element in _expected) { | |
127 ++expectedLength; | |
128 } | |
129 } | |
130 var actualLength = 0; | |
131 if (item is Collection) { | |
132 actualLength = item.length; | |
133 } else { | |
134 for (var element in item) { | |
135 ++actualLength; | |
136 } | |
137 } | |
138 if (expectedLength > actualLength) { | |
139 return 'has too few elements (${actualLength} < ${expectedLength})'; | |
140 } else if (expectedLength < actualLength) { | |
141 return 'has too many elements (${actualLength} > ${expectedLength})'; | |
142 } | |
143 List<bool> matched = new List<bool>(actualLength); | |
144 for (var i = 0; i < actualLength; i++) { | |
145 matched[i] = false; | |
146 } | |
147 var expectedPosition = 0; | |
148 for (var expectedElement in _expected) { | |
149 var actualPosition = 0; | |
150 var gotMatch = false; | |
151 for (var actualElement in item) { | |
152 if (!matched[actualPosition]) { | |
153 if (expectedElement == actualElement) { | |
154 matched[actualPosition] = gotMatch = true; | |
155 break; | |
156 } | |
157 } | |
158 ++actualPosition; | |
159 } | |
160 if (!gotMatch) { | |
161 Description reason = new StringDescription(); | |
162 reason.add('has no match for element '). | |
163 addDescriptionOf(expectedElement). | |
164 add(' at position ${expectedPosition}'); | |
165 return reason.toString(); | |
166 } | |
167 ++expectedPosition; | |
168 } | |
169 return null; | |
170 } | |
171 | |
172 bool matches(item, MatchState mismatchState) => (_test(item) == null); | |
173 | |
174 Description describe(Description description) => | |
175 description.add('equals ').addDescriptionOf(_expected).add(' unordered'); | |
176 | |
177 Description describeMismatch(item, Description mismatchDescription, | |
178 MatchState matchState, bool verbose) => | |
179 mismatchDescription.add(_test(item)); | |
180 } | |
181 | |
182 /** | |
183 * Collection matchers match against [Collection]s. We add this intermediate | |
184 * class to give better mismatch error messages than the base Matcher class. | |
185 */ | |
186 /* abstract */ class _CollectionMatcher extends BaseMatcher { | |
187 const _CollectionMatcher(); | |
188 Description describeMismatch(item, Description mismatchDescription, | |
189 MatchState matchState, bool verbose) { | |
190 if (item is !Collection) { | |
191 return mismatchDescription. | |
192 addDescriptionOf(item). | |
193 add(' not a collection'); | |
194 } else { | |
195 return super.describeMismatch(item, mismatchDescription, matchState, | |
196 verbose); | |
197 } | |
198 } | |
199 } | |
OLD | NEW |