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