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 |