Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: pkg/unittest/lib/src/core_matchers.dart

Issue 184103002: Support nested matchers in deep equality matching. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/unittest/lib/src/pretty_print.dart » ('j') | pkg/unittest/lib/src/pretty_print.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698