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