| 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 |