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 part of unittest.matcher; | 5 part of unittest.matcher; |
6 | 6 |
7 /** | 7 /** |
8 * Returns a matcher that matches empty strings, maps or iterables | 8 * Returns a matcher that matches empty strings, maps or iterables |
9 * (including collections). | 9 * (including collections). |
10 */ | 10 */ |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 | 92 |
93 class _DeepMatcher extends Matcher { | 93 class _DeepMatcher extends Matcher { |
94 final _expected; | 94 final _expected; |
95 final int _limit; | 95 final int _limit; |
96 var count; | 96 var count; |
97 | 97 |
98 _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit; | 98 _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit; |
99 | 99 |
100 // Returns a pair (reason, location) | 100 // Returns a pair (reason, location) |
101 List _compareIterables(expected, actual, matcher, depth, location) { | 101 List _compareIterables(expected, actual, matcher, depth, location) { |
102 if (actual is !Iterable) { | 102 if (actual is! Iterable) { |
103 return ['is not Iterable', location]; | 103 return ['is not Iterable', location]; |
104 } | 104 } |
105 var expectedIterator = expected.iterator; | 105 var expectedIterator = expected.iterator; |
106 var actualIterator = actual.iterator; | 106 var actualIterator = actual.iterator; |
107 var index = 0; | 107 var index = 0; |
108 while (true) { | 108 while (true) { |
109 var newLocation = '${location}[${index}]'; | 109 var newLocation = '${location}[${index}]'; |
110 if (expectedIterator.moveNext()) { | 110 if (expectedIterator.moveNext()) { |
111 if (actualIterator.moveNext()) { | 111 if (actualIterator.moveNext()) { |
112 var rp = matcher(expectedIterator.current, | 112 var rp = matcher(expectedIterator.current, |
(...skipping 13 matching lines...) Expand all Loading... |
126 return null; | 126 return null; |
127 } | 127 } |
128 | 128 |
129 List _recursiveMatch(expected, actual, String location, int depth) { | 129 List _recursiveMatch(expected, actual, String location, int depth) { |
130 String reason = null; | 130 String reason = null; |
131 // If _limit is 1 we can only recurse one level into object. | 131 // If _limit is 1 we can only recurse one level into object. |
132 bool canRecurse = depth == 0 || _limit > 1; | 132 bool canRecurse = depth == 0 || _limit > 1; |
133 bool equal; | 133 bool equal; |
134 try { | 134 try { |
135 equal = (expected == actual); | 135 equal = (expected == actual); |
136 } catch (e,s) { | 136 } catch (e, s) { |
137 // TODO(gram): Add a test for this case. | 137 // TODO(gram): Add a test for this case. |
138 reason = '== threw "$e"'; | 138 reason = '== threw "$e"'; |
139 return [reason, location]; | 139 return [reason, location]; |
140 } | 140 } |
141 if (equal) { | 141 if (equal) { |
142 // Do nothing. | 142 // Do nothing. |
143 } else if (depth > _limit) { | 143 } else if (depth > _limit) { |
144 reason = 'recursion depth limit exceeded'; | 144 reason = 'recursion depth limit exceeded'; |
145 } else { | 145 } else { |
146 if (expected is Iterable && canRecurse) { | 146 if (expected is Iterable && canRecurse) { |
147 List result = _compareIterables(expected, actual, | 147 List result = _compareIterables(expected, actual, |
148 _recursiveMatch, depth + 1, location); | 148 _recursiveMatch, depth + 1, location); |
149 if (result != null) { | 149 if (result != null) { |
150 reason = result[0]; | 150 reason = result[0]; |
151 location = result[1]; | 151 location = result[1]; |
152 } | 152 } |
153 } else if (expected is Map && canRecurse) { | 153 } else if (expected is Map && canRecurse) { |
154 if (actual is !Map) { | 154 if (actual is! Map) { |
155 reason = 'expected a map'; | 155 reason = 'expected a map'; |
156 } else { | 156 } else { |
157 var err = (expected.length == actual.length) ? '' : | 157 var err = (expected.length == actual.length) ? '' : |
158 'has different length and '; | 158 'has different length and '; |
159 for (var key in expected.keys) { | 159 for (var key in expected.keys) { |
160 if (!actual.containsKey(key)) { | 160 if (!actual.containsKey(key)) { |
161 reason = '${err}is missing map key \'$key\''; | 161 reason = '${err}is missing map key \'$key\''; |
162 break; | 162 break; |
163 } | 163 } |
164 } | 164 } |
165 if (reason == null) { | 165 if (reason == null) { |
166 for (var key in actual.keys) { | 166 for (var key in actual.keys) { |
167 if (!expected.containsKey(key)) { | 167 if (!expected.containsKey(key)) { |
168 reason = '${err}has extra map key \'$key\''; | 168 reason = '${err}has extra map key \'$key\''; |
169 break; | 169 break; |
170 } | 170 } |
171 } | 171 } |
172 if (reason == null) { | 172 if (reason == null) { |
173 for (var key in expected.keys) { | 173 for (var key in expected.keys) { |
174 var rp = _recursiveMatch(expected[key], actual[key], | 174 var rp = _recursiveMatch(expected[key], actual[key], |
175 "${location}['${key}']", depth+1); | 175 "${location}['${key}']", depth + 1); |
176 if (rp != null) { | 176 if (rp != null) { |
177 reason = rp[0]; | 177 reason = rp[0]; |
178 location = rp[1]; | 178 location = rp[1]; |
179 break; | 179 break; |
180 } | 180 } |
181 } | 181 } |
182 } | 182 } |
183 } | 183 } |
184 } | 184 } |
185 } else { | 185 } else { |
186 var description = new StringDescription(); | 186 var description = new StringDescription(); |
187 // If we have recursed, show the expected value too; if not, | 187 // If we have recursed, show the expected value too; if not, |
188 // expect() will show it for us. | 188 // expect() will show it for us. |
189 if (depth > 0) { | 189 if (depth > 0) { |
190 description.add('was '). | 190 description.add('was '). |
191 addDescriptionOf(actual). | 191 addDescriptionOf(actual). |
192 add(' instead of '). | 192 add(' instead of '). |
193 addDescriptionOf(expected); | 193 addDescriptionOf(expected); |
194 reason = description.toString(); | 194 reason = description.toString(); |
195 } else { | 195 } else { |
196 reason = ''; // We're not adding any value to the actual value. | 196 reason = ''; // We're not adding any value to the actual value. |
197 } | 197 } |
198 } | 198 } |
199 } | 199 } |
200 if (reason == null) return null; | 200 if (reason == null) return null; |
201 return [reason, location]; | 201 return [reason, location]; |
202 } | 202 } |
203 | 203 |
204 String _match(expected, actual, Map matchState) { | 204 String _match(expected, actual, Map matchState) { |
205 var rp = _recursiveMatch(expected, actual, '', 0); | 205 var rp = _recursiveMatch(expected, actual, '', 0); |
206 if (rp == null) return null; | 206 if (rp == null) return null; |
207 var reason; | 207 var reason; |
208 if (rp[0].length > 0) { | 208 if (rp[0].length > 0) { |
209 if (rp[1].length > 0) { | 209 if (rp[1].length > 0) { |
210 reason = "${rp[0]} at location ${rp[1]}"; | 210 reason = "${rp[0]} at location ${rp[1]}"; |
211 } else { | 211 } else { |
212 reason = rp[0]; | 212 reason = rp[0]; |
213 } | 213 } |
214 } else { | 214 } else { |
215 reason = ''; | 215 reason = ''; |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 }); | 431 }); |
432 }); | 432 }); |
433 // It hasn't failed yet. | 433 // It hasn't failed yet. |
434 return true; | 434 return true; |
435 } | 435 } |
436 | 436 |
437 try { | 437 try { |
438 item(); | 438 item(); |
439 return false; | 439 return false; |
440 } catch (e, s) { | 440 } catch (e, s) { |
441 if (_matcher == null ||_matcher.matches(e, matchState)) { | 441 if (_matcher == null || _matcher.matches(e, matchState)) { |
442 return true; | 442 return true; |
443 } else { | 443 } else { |
444 addStateInfo(matchState, {'exception': e, 'stack': s}); | 444 addStateInfo(matchState, {'exception': e, 'stack': s}); |
445 return false; | 445 return false; |
446 } | 446 } |
447 } | 447 } |
448 } | 448 } |
449 | 449 |
450 Description describe(Description description) { | 450 Description describe(Description description) { |
451 if (_matcher == null) { | 451 if (_matcher == null) { |
(...skipping 433 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
885 addDescriptionOf(matchState['feature']); | 885 addDescriptionOf(matchState['feature']); |
886 var innerDescription = new StringDescription(); | 886 var innerDescription = new StringDescription(); |
887 _matcher.describeMismatch(matchState['feature'], innerDescription, | 887 _matcher.describeMismatch(matchState['feature'], innerDescription, |
888 matchState['state'], verbose); | 888 matchState['state'], verbose); |
889 if (innerDescription.length > 0) { | 889 if (innerDescription.length > 0) { |
890 mismatchDescription.add(' which ').add(innerDescription.toString()); | 890 mismatchDescription.add(' which ').add(innerDescription.toString()); |
891 } | 891 } |
892 return mismatchDescription; | 892 return mismatchDescription; |
893 } | 893 } |
894 } | 894 } |
OLD | NEW |