Chromium Code Reviews| 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) return ['is not Iterable', location]; |
| 103 return ['is not Iterable', location]; | 103 |
| 104 } | |
| 105 var expectedIterator = expected.iterator; | 104 var expectedIterator = expected.iterator; |
| 106 var actualIterator = actual.iterator; | 105 var actualIterator = actual.iterator; |
| 107 var index = 0; | 106 var index = 0; |
| 108 while (true) { | 107 while (true) { |
| 109 var newLocation = '${location}[${index}]'; | 108 var newLocation = '${location}[${index}]'; |
| 110 if (expectedIterator.moveNext()) { | 109 if (expectedIterator.moveNext()) { |
| 111 if (actualIterator.moveNext()) { | 110 if (actualIterator.moveNext()) { |
| 112 var rp = matcher(expectedIterator.current, | 111 var rp = matcher(expectedIterator.current, actualIterator.current, |
| 113 actualIterator.current, newLocation, | 112 newLocation, depth); |
| 114 depth); | |
| 115 if (rp != null) return rp; | 113 if (rp != null) return rp; |
| 116 ++index; | 114 index++; |
| 117 } else { | 115 } else { |
| 118 return ['shorter than expected', newLocation]; | 116 return ['shorter than expected', newLocation]; |
| 119 } | 117 } |
| 120 } else if (actualIterator.moveNext()) { | 118 } else if (actualIterator.moveNext()) { |
| 121 return ['longer than expected', newLocation]; | 119 return ['longer than expected', newLocation]; |
| 122 } else { | 120 } else { |
| 123 return null; | 121 return null; |
| 124 } | 122 } |
|
Siggi Cherem (dart-lang)
2014/02/28 02:41:04
since you are already fixing style :), consider ch
Bob Nystrom
2014/02/28 17:31:24
Done, and then some.
| |
| 125 } | 123 } |
| 126 return null; | |
| 127 } | 124 } |
| 128 | 125 |
| 129 List _recursiveMatch(expected, actual, String location, int depth) { | 126 List _recursiveMatch(expected, actual, String location, int depth) { |
| 130 String reason = null; | 127 // If the expected value is a matcher, try to match it. |
| 128 if (expected is Matcher) { | |
| 129 var matchState = {}; | |
| 130 if (expected.matches(actual, matchState)) return null; | |
| 131 | |
| 132 var description = new StringDescription(); | |
| 133 expected.describe(description); | |
| 134 return ['does not match $description', location]; | |
| 135 } else { | |
| 136 // Otherwise, test for equality. | |
| 137 try { | |
| 138 if (expected == actual) return null; | |
| 139 } catch (e, s) { | |
| 140 // TODO(gram): Add a test for this case. | |
| 141 return ['== threw "$e"', location]; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 if (depth > _limit) { | |
| 146 return ['recursion depth limit exceeded', location]; | |
| 147 } | |
| 148 | |
| 131 // If _limit is 1 we can only recurse one level into object. | 149 // If _limit is 1 we can only recurse one level into object. |
| 132 bool canRecurse = depth == 0 || _limit > 1; | 150 bool canRecurse = depth == 0 || _limit > 1; |
| 133 bool equal; | 151 |
| 134 try { | 152 if (expected is Iterable && canRecurse) { |
| 135 equal = (expected == actual); | 153 return _compareIterables(expected, actual, _recursiveMatch, depth + 1, |
| 136 } catch (e, s) { | 154 location); |
| 137 // TODO(gram): Add a test for this case. | |
| 138 reason = '== threw "$e"'; | |
| 139 return [reason, location]; | |
| 140 } | 155 } |
| 141 if (equal) { | 156 |
| 142 // Do nothing. | 157 if (expected is Map && canRecurse) { |
| 143 } else if (depth > _limit) { | 158 if (actual is! Map) { |
| 144 reason = 'recursion depth limit exceeded'; | 159 return ['expected a map', location]; |
|
Siggi Cherem (dart-lang)
2014/02/28 02:41:04
nit: this might fit the 1-line if:
if(...) retu
Bob Nystrom
2014/02/28 17:31:24
Done.
| |
| 145 } else { | 160 } |
| 146 if (expected is Iterable && canRecurse) { | 161 |
| 147 List result = _compareIterables(expected, actual, | 162 var err = (expected.length == actual.length) ? '' : |
| 148 _recursiveMatch, depth + 1, location); | 163 'has different length and '; |
| 149 if (result != null) { | 164 for (var key in expected.keys) { |
| 150 reason = result[0]; | 165 if (!actual.containsKey(key)) { |
| 151 location = result[1]; | 166 return ["${err}is missing map key '$key'", location]; |
| 152 } | |
| 153 } else if (expected is Map && canRecurse) { | |
| 154 if (actual is! Map) { | |
| 155 reason = 'expected a map'; | |
| 156 } else { | |
| 157 var err = (expected.length == actual.length) ? '' : | |
| 158 'has different length and '; | |
| 159 for (var key in expected.keys) { | |
| 160 if (!actual.containsKey(key)) { | |
| 161 reason = '${err}is missing map key \'$key\''; | |
| 162 break; | |
| 163 } | |
| 164 } | |
| 165 if (reason == null) { | |
| 166 for (var key in actual.keys) { | |
| 167 if (!expected.containsKey(key)) { | |
| 168 reason = '${err}has extra map key \'$key\''; | |
| 169 break; | |
| 170 } | |
| 171 } | |
| 172 if (reason == null) { | |
| 173 for (var key in expected.keys) { | |
| 174 var rp = _recursiveMatch(expected[key], actual[key], | |
| 175 "${location}['${key}']", depth + 1); | |
| 176 if (rp != null) { | |
| 177 reason = rp[0]; | |
| 178 location = rp[1]; | |
| 179 break; | |
| 180 } | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 } else { | |
| 186 var description = new StringDescription(); | |
| 187 // If we have recursed, show the expected value too; if not, | |
| 188 // expect() will show it for us. | |
| 189 if (depth > 0) { | |
| 190 description.add('was '). | |
| 191 addDescriptionOf(actual). | |
| 192 add(' instead of '). | |
| 193 addDescriptionOf(expected); | |
| 194 reason = description.toString(); | |
| 195 } else { | |
| 196 reason = ''; // We're not adding any value to the actual value. | |
| 197 } | 167 } |
| 198 } | 168 } |
| 169 | |
| 170 for (var key in actual.keys) { | |
| 171 if (!expected.containsKey(key)) { | |
| 172 return ["${err}has extra map key '$key'", location]; | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 for (var key in expected.keys) { | |
| 177 var rp = _recursiveMatch(expected[key], actual[key], | |
| 178 "${location}['${key}']", depth + 1); | |
| 179 if (rp != null) return rp; | |
| 180 } | |
| 181 | |
| 182 return null; | |
| 199 } | 183 } |
| 200 if (reason == null) return null; | 184 |
| 201 return [reason, location]; | 185 var description = new StringDescription(); |
| 186 | |
| 187 // If we have recursed, show the expected value too; if not, expect() will | |
| 188 // show it for us. | |
| 189 if (depth > 0) { | |
| 190 description.add('was '). | |
| 191 addDescriptionOf(actual). | |
| 192 add(' instead of '). | |
| 193 addDescriptionOf(expected); | |
| 194 return [description.toString(), location]; | |
| 195 } | |
| 196 | |
| 197 // We're not adding any value to the actual value. | |
| 198 return ["", location]; | |
| 202 } | 199 } |
| 203 | 200 |
| 204 String _match(expected, actual, Map matchState) { | 201 String _match(expected, actual, Map matchState) { |
| 205 var rp = _recursiveMatch(expected, actual, '', 0); | 202 var rp = _recursiveMatch(expected, actual, '', 0); |
| 206 if (rp == null) return null; | 203 if (rp == null) return null; |
| 207 var reason; | 204 var reason; |
| 208 if (rp[0].length > 0) { | 205 if (rp[0].length > 0) { |
| 209 if (rp[1].length > 0) { | 206 if (rp[1].length > 0) { |
| 210 reason = "${rp[0]} at location ${rp[1]}"; | 207 reason = "${rp[0]} at location ${rp[1]}"; |
| 211 } else { | 208 } else { |
| (...skipping 673 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 885 addDescriptionOf(matchState['feature']); | 882 addDescriptionOf(matchState['feature']); |
| 886 var innerDescription = new StringDescription(); | 883 var innerDescription = new StringDescription(); |
| 887 _matcher.describeMismatch(matchState['feature'], innerDescription, | 884 _matcher.describeMismatch(matchState['feature'], innerDescription, |
| 888 matchState['state'], verbose); | 885 matchState['state'], verbose); |
| 889 if (innerDescription.length > 0) { | 886 if (innerDescription.length > 0) { |
| 890 mismatchDescription.add(' which ').add(innerDescription.toString()); | 887 mismatchDescription.add(' which ').add(innerDescription.toString()); |
| 891 } | 888 } |
| 892 return mismatchDescription; | 889 return mismatchDescription; |
| 893 } | 890 } |
| 894 } | 891 } |
| OLD | NEW |