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

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

Issue 10441104: New expectation functions plus convert old tests to use these. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /** Returns a matcher that matches any null value. */
6 IMatcher isNull() => new _IsNull();
Bob Nystrom 2012/05/30 23:23:51 Make it a var: final isNull = const _IsNull(); P
gram 2012/06/01 17:33:15 Done.
7
8 /** Returns a matcher that matches any non-null value. */
9 IMatcher isNotNull() => isNot(isNull());
10
11 class _IsNull extends Matcher {
12 bool matches(item) => (item == null);
Bob Nystrom 2012/05/30 23:23:51 Remove parens: bool matches(item) => item == nul
gram 2012/06/01 17:33:15 Done.
13 IDescription describe(IDescription description) => description.append('null');
14 }
15
16 /** Returns a matcher that matches the Boolean value true. */
17 IMatcher isTrue() => new _IsTrue();
18
19 /** Returns a matcher that matches anything except the Boolean value true. */
20 IMatcher isFalse() => isNot(isTrue());
21
22 class _IsTrue extends Matcher {
23
Bob Nystrom 2012/05/30 23:23:51 Delete this blank line.
gram 2012/06/01 17:33:15 Done.
24 bool matches(item) => (item == true);
Bob Nystrom 2012/05/30 23:23:51 Remove unneeded parens.
gram 2012/06/01 17:33:15 Done.
25
26 IDescription describe(IDescription description) => description.append('true');
27 }
28
29 /** Returns a matches that matches if the value is the same instance
Bob Nystrom 2012/05/30 23:23:51 Can you format this with the text on the next line
gram 2012/06/01 17:33:15 Done.
30 * as [object] (===).
Bob Nystrom 2012/05/30 23:23:51 Put === in backticks to code format it: as [object
gram 2012/06/01 17:33:15 Done.
31 */
32 IMatcher same(object) => new _IsSame(object);
Bob Nystrom 2012/05/30 23:23:51 "isSameAs"?
gram 2012/06/01 17:33:15 Done.
33
34 class _IsSame extends Matcher {
35
36 var _object;
Bob Nystrom 2012/05/30 23:23:51 Other matcher classes use "_val" for this. Should
gram 2012/06/01 17:33:15 Done.
37
38 _IsSame(this._object);
39
40 bool matches(item) => (item === _object);
Bob Nystrom 2012/05/30 23:23:51 Unnecessary ().
gram 2012/06/01 17:33:15 Done.
41
42 // If all types were hashable we could show a hash here
Bob Nystrom 2012/05/30 23:23:51 True. :( Add a "." at the end of the sentence.
gram 2012/06/01 17:33:15 Done.
43 IDescription describe(IDescription description) =>
44 description.append('same instance as ').appendDescriptionOf(_object);
45 }
46
47 /** Returns a matcher that matches if two objects are equal (==). */
48 IMatcher equals(obj) => new _IsEqual(obj);
49
50 class _IsEqual extends Matcher {
51 var _object;
52
53 _IsEqual(this._object);
54
55 bool matches(item) => (item == _object);
56
57 IDescription describe(IDescription description) {
58 //if (_object is IMatcher) {
Bob Nystrom 2012/05/30 23:23:51 Remove commented out code.
gram 2012/06/01 17:33:15 Done.
59 // return description.append('<').
60 // appendDescriptionOf(_object).
61 // append('>');
62 //} else {
63 return description.appendDescriptionOf(_object);
64 //}
65 }
66 }
67
68 /** Returns a matcher that matches any value. */
69 IMatcher anything([description = 'anything']) => new _IsAnything(description);
Bob Nystrom 2012/05/30 23:23:51 Can we get rid of description and make this a cons
gram 2012/06/01 17:33:15 Done.
70
71 class _IsAnything extends Matcher {
72 var _description;
73
74 _IsAnything([this._description = 'anything']);
75
76 bool matches(item) => true;
77
78 IDescription describe(IDescription description) =>
79 description.append(_description);
80 }
81
82 /**
83 * Returns a matcher that matches functions that throw exceptions when called.
84 * The value passed to expect() should be a reference to the function.
85 * Note that the function cannot take arguments; to handle this
86 * a wrapper will have to be created.
87 * The function will be called once.
88 */
89 IMatcher throwsException() => new _Throws();
Bob Nystrom 2012/05/30 23:23:51 "throwsException" -> "throws"?
gram 2012/06/01 17:33:15 Done.
90
91 /**
92 * Returns a matcher that matches a function call against an exception,
93 * which is in turn constrained by a [matcher].
94 * The value passed to expect() should be a reference to the function.
95 * Note that the function cannot take arguments; to handle this
96 * a wrapper will have to be created.
97 * The function will be called once upon success, or twice upon failure
98 * (the second time to get the failure description).
99 */
100 IMatcher throwsExceptionWhich(IMatcher matcher) => new _Throws(matcher);
101 /**
Bob Nystrom 2012/05/30 23:23:51 Add blank line above comment.
gram 2012/06/01 17:33:15 Done.
102 * Returns a matcher that matches a function call against no exception.
103 * The function will be called once. Any exceptions will be silently swallowed.
104 * The value passed to expect() should be a reference to the function.
105 * Note that the function cannot take arguments; to handle this
106 * a wrapper will have to be created.
107 */
108 IMatcher returnsNormally() => new _ReturnsNormally();
Bob Nystrom 2012/05/30 23:23:51 Maybe I don't the intent here, but it seems to me
gram 2012/06/01 17:33:15 Yes - this will catch an exception and return fals
Bob Nystrom 2012/06/01 18:22:22 What happens when exceptions are thrown outside of
109
110 class _Throws extends Matcher {
111 IMatcher _matcher;
112
113 _Throws([IMatcher this._matcher = null]) {
114 if (_matcher != null && !(_matcher is Matcher)) {
Bob Nystrom 2012/05/30 23:23:51 You can delete this. Dart will validate this for y
gram 2012/06/01 17:33:15 Done.
gram 2012/06/01 17:33:15 Done.
115 throw new IllegalArgumentException('Throws parameter must be matcher');
116 }
117 }
118
119 bool matches(item) {
120 try {
121 item();
122 return false;
123 } catch (final e) {
124 return _matcher == null || _matcher.matches(e);
125 }
126 }
127
128 IDescription describe(IDescription description) {
129 if (_matcher == null) {
130 return description.append("throws an exception");
131 } else {
132 return description.append('throws an exception which needs to match ').
Bob Nystrom 2012/05/30 23:23:51 "which needs to match" -> "which matches"?
gram 2012/06/01 17:33:15 Done.
133 appendDescriptionOf(_matcher);
134 }
135 }
136
137 IDescription describeMismatch(item, IDescription mismatchDescription) {
138 try {
139 item();
140 return mismatchDescription.append(' no exception');
141 } catch (final e) {
142 return mismatchDescription.append(' exception does not match ').
143 appendDescriptionOf(_matcher);
144 }
145 }
146 }
147
148 class _ReturnsNormally extends Matcher {
149 bool matches(f) {
150 try {
151 f();
152 return true;
153 } catch (final e) {
154 return false;
155 }
156 }
157 IDescription describe(IDescription description) =>
158 description.append("return normally");
159 IDescription describeMismatch(item, IDescription mismatchDescription) {
160 return mismatchDescription.append(' threw exception');
161 }
162 }
163
164 /**
165 * Returns a matcher that matches if an object is an instance
166 * of a (super)[type].
Bob Nystrom 2012/05/30 23:23:51 This is worded a bit confusingly. Maybe: "if an o
gram 2012/06/01 17:33:15 Done.
167 *
168 * As types are not first class objects in Dart we can only
169 * approximate this test, by using a generic wrapper named Type.
Bob Nystrom 2012/05/30 23:23:51 Delete ","
gram 2012/06/01 17:33:15 Done.
170 *
171 * For example, to test whether 'bar' is an instance of type
172 * 'Foo', we would write:
173 *
174 * expect(bar, instanceOf(new Type<Foo>()));
175 *
176 * To get better error message, supply a name when creating the
177 * Type wrapper; e.g.:
178 *
179 * expect(bar, instanceOf(new Type<Foo>('Foo')));
180 */
181 IMatcher isInstanceOf(Type type) => new _IsInstanceOf(type);
182
183 class Type<T> {
Bob Nystrom 2012/05/30 23:23:51 How about making this implement IMatcher directly
gram 2012/06/01 17:33:15 Done.
184 String _name;
185 Type([this._name = 'specified type']);
186 bool isInstance(obj) => obj is T;
187 String toString() => _name;
188 }
189
190 class _IsInstanceOf extends Matcher {
191
192 Type _expected_type;
Bob Nystrom 2012/05/30 23:23:51 "_expectedType"
gram 2012/06/01 17:33:15 No longer applicable.
193
194 _IsInstanceOf(Type this._expected_type) {
195 if (!(_expected_type is Type)) {
196 throw new IllegalArgumentException('IsInstanceOf requires type');
197 }
198 }
199
200 bool matches(item) => _expected_type.isInstance(item);
201
202 // The description here is lame :-(
Bob Nystrom 2012/05/30 23:23:51 Why?
gram 2012/06/01 17:33:15 Because we don't have the type name unless it is e
203 IDescription describe(IDescription description) =>
204 description.append('an instance of ${_expected_type.toString()}');
Bob Nystrom 2012/05/30 23:23:51 You don't need .toString() here. Interpolation doe
gram 2012/06/01 17:33:15 Done.
205 }
206
207 /**
208 * Returns a matcher that matches if an object has a length property
209 * that matches [matcher].
Bob Nystrom 2012/05/30 23:23:51 This doesn't seem to add a lot of value. Why not j
gram 2012/06/01 17:33:15 You can make this a bit shorter, with: expect(foo
Bob Nystrom 2012/06/01 18:22:22 I like terseness, but I also like simplicity and m
210 */
211 IMatcher hasLength([matcher = null]) =>
212 new _HasLength(matcher == null ? anything() : wrapMatcher(matcher));
213
214 // TODO(gram) - this is intended to fail if the value
215 // does not have a length property but it doesn't. See if this
216 // can be fixed.
217 class _HasLength extends Matcher {
218
219 var _len_matcher;
Bob Nystrom 2012/05/30 23:23:51 "_lenMatcher"
gram 2012/06/01 17:33:15 Done.
220
221 _HasLength(Matcher this._len_matcher) { }
222 bool matches(item) {
223 try {
224 // TODO(gram): this hack to throw if no length property is not
225 // working
226 if ((item.length * item.length) >= 0) {
227 return _len_matcher.matches(item.length);
228 }
229 } catch(var e) {
230 return false;
231 }
232 }
233
234 IDescription describeMismatch(item, IDescription mismatchDescription) {
235 super.describeMismatch(item, mismatchDescription);
236 try {
237 // TODO(gram): this hack to throw if no length property is not
238 // working
239 if ((item.length * item.length) >= 0) {
240 return mismatchDescription.append(' with length of ').
241 appendDescriptionOf(item.length);
242 }
243 } catch (var e) {
244 return mismatchDescription.append(' has no length property');
245 }
246 }
247
248 IDescription describe(IDescription description) =>
249 description.append('an object with length of ').
250 appendDescriptionOf(_len_matcher);
251 }
252
253 /** Returns a matcher that matches if value.toString() matches [matcher] */
254 IMatcher hasString(matcher) => new _HasString(wrapMatcher(matcher));
Bob Nystrom 2012/05/30 23:23:51 This name is confusing to me, and it doesn't seem
gram 2012/06/01 17:33:15 Done.
255
256 class _HasString extends Matcher {
257 var _str_matcher;
258
259 _HasString(this._str_matcher);
260
261 bool matches(item) => _str_matcher.matches(item.toString());
262
263 IDescription describe(IDescription description) =>
264 description.append('with toString() value ').
265 appendDescriptionOf(_str_matcher);
266 }
267
268 /**
269 * Returns a matcher that does a deep recursive match. This only works
270 * with built-in types. Note that cyclic structures will never terminate!
Bob Nystrom 2012/05/30 23:23:51 Wouldn't this also work with user-defined types th
gram 2012/06/01 17:33:15 I made it a bit more general, and added an item co
271 */
272 IMatcher recursivelyMatches(obj) => new _DeepMatcher(obj);
273
274 class _DeepMatcher extends Matcher {
275 var _obj;
276
277 _DeepMatcher(this._obj) {}
278
279 String _recursiveMatch(expected, actual, String location) {
280 String reason = null;
281 if (expected is Collection) {
282 if (!(actual is Collection)) {
283 reason = 'expected a collection';
284 } else if (expected.length != actual.length) {
285 reason = 'different collection lengths';
286 } else {
287 for (var i = 0; i < expected.length; i++) {
288 reason = _recursiveMatch(expected[i], actual[i],
289 'at position ${i} ${location}');
Bob Nystrom 2012/05/30 23:23:51 Indent another 2 spaces. Continued lines are inden
gram 2012/06/01 17:33:15 Done.
290 if (reason != null) {
291 break;
292 }
293 }
294 }
295 } else if (expected is Map) {
296 if (!(actual is Map)) {
Bob Nystrom 2012/05/30 23:23:51 actual is !Map
gram 2012/06/01 17:33:15 Done.
297 reason = 'expected a map';
298 } else if (expected.length != actual.length) {
299 reason = 'different map lengths';
300 } else {
301 for (var key in expected.getKeys()) {
302 if (!actual.containsKey(key)) {
303 reason = 'missing map key ${key}';
304 break;
305 }
306 reason = _recursiveMatch(expected[key], actual[key],
307 'with key ${key} ${location}');
308 if (reason != null) {
309 break;
310 }
311 }
312 }
313 } else {
314 if (expected != actual) {
315 reason = 'expected ${expected} but got ${actual}';
316 }
317 }
318 if (reason == null) {
319 return null;
320 } else {
321 return '${reason} ${location}';
322 }
323 }
324
325 bool matches(item) => (_recursiveMatch(_obj, item, '') == null);
326
327 IDescription describe(IDescription description) =>
328 description.append('recursively matches ').
329 appendDescriptionOf(_obj);
330
331
332 IDescription describeMismatch(item, IDescription mismatchDescription) =>
333 mismatchDescription.append(_recursiveMatch(_obj, item, ''));
334 }
335
336
337
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698