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

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
« no previous file with comments | « lib/unittest/collection_matchers.dart ('k') | lib/unittest/description.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
6 /**
7 * Returns a matcher that matches empty strings, maps or collections.
8 */
9 final Matcher isEmpty = const _Empty();
10
11 class _Empty extends BaseMatcher {
12 const _Empty();
13 bool matches(item) {
14 if (item is Map || item is Collection) {
15 return item.isEmpty();
16 } else if (item is String) {
17 return item.length == 0;
18 } else {
19 return false;
20 }
21 }
22 Description describe(Description description) =>
23 description.add('empty');
24 }
25
26 /** A matcher that matches any null value. */
27 final Matcher isNull = const _IsNull();
28
29 /** A matcher that matches any non-null value. */
30 final Matcher isNotNull = const _IsNotNull();
31
32 class _IsNull extends BaseMatcher {
33 const _IsNull();
34 bool matches(item) => item == null;
35 Description describe(Description description) =>
36 description.add('null');
37 }
38
39 class _IsNotNull extends BaseMatcher {
40 const _IsNotNull();
41 bool matches(item) => item != null;
42 Description describe(Description description) =>
43 description.add('not null');
44 }
45
46 /** A matcher that matches the Boolean value true. */
47 final Matcher isTrue = const _IsTrue();
48
49 /** A matcher that matches anything except the Boolean value true. */
50 final Matcher isFalse = const _IsFalse();
51
52 class _IsTrue extends BaseMatcher {
53 const _IsTrue();
54 bool matches(item) => item == true;
55 Description describe(Description description) =>
56 description.add('true');
57 }
58
59 class _IsFalse extends BaseMatcher {
60 const _IsFalse();
61 bool matches(item) => item != true;
62 Description describe(Description description) =>
63 description.add('false');
64 }
65
66 /**
67 * Returns a matches that matches if the value is the same instance
68 * as [object] (`===`).
69 */
70 Matcher same(expected) => new _IsSameAs(expected);
71
72 class _IsSameAs extends BaseMatcher {
73 final _expected;
74 const _IsSameAs(this._expected);
75 bool matches(item) => item === _expected;
76 // If all types were hashable we could show a hash here.
77 Description describe(Description description) =>
78 description.add('same instance as ').addDescriptionOf(_expected);
79 }
80
81 /** Returns a matcher that matches if two objects are equal (==). */
82 Matcher equals(expected) => new _IsEqual(expected);
83
84 class _IsEqual extends BaseMatcher {
85 final _expected;
86 const _IsEqual(this._expected);
87 bool matches(item) => item == _expected;
88 Description describe(Description description) =>
89 description.addDescriptionOf(_expected);
90 }
91
92 /** A matcher that matches any value. */
93 final Matcher anything = const _IsAnything();
94
95 class _IsAnything extends BaseMatcher {
96 const _IsAnything();
97 bool matches(item) => true;
98 Description describe(Description description) =>
99 description.add('anything');
100 }
101
102 /**
103 * Returns a matcher that matches if an object is an instance
104 * of [type] (or a subtype).
105 *
106 * As types are not first class objects in Dart we can only
107 * approximate this test by using a generic wrapper class.
108 *
109 * For example, to test whether 'bar' is an instance of type
110 * 'Foo', we would write:
111 *
112 * expect(bar, new isInstanceOf<Foo>());
113 *
114 * To get better error message, supply a name when creating the
115 * Type wrapper; e.g.:
116 *
117 * expect(bar, new isInstanceOf<Foo>('Foo'));
118 */
119 class isInstanceOf<T> extends BaseMatcher {
120 final String _name;
121 const isInstanceOf([this._name = 'specified type']);
122 bool matches(obj) => obj is T;
123 // The description here is lame :-(
124 Description describe(Description description) =>
125 description.add('an instance of ${_name}');
126 }
127
128 /**
129 * A matcher that matches functions that throw exceptions when called.
130 * The value passed to expect() should be a reference to the function.
131 * Note that the function cannot take arguments; to handle this
132 * a wrapper will have to be created.
133 * The function will be called once upon success, or twice upon failure
134 * (the second time to get the failure description).
135 */
136 final Matcher throws = const _Throws();
137
138 /**
139 * Returns a matcher that matches a function call against an exception,
140 * which is in turn constrained by a [matcher].
141 * The value passed to expect() should be a reference to the function.
142 * Note that the function cannot take arguments; to handle this
143 * a wrapper will have to be created.
144 * The function will be called once upon success, or twice upon failure
145 * (the second time to get the failure description).
146 */
147 Matcher throwsA(Matcher matcher) => new _Throws(matcher);
148
149 /**
150 * A matcher that matches a function call against no exception.
151 * The function will be called once. Any exceptions will be silently swallowed.
152 * The value passed to expect() should be a reference to the function.
153 * Note that the function cannot take arguments; to handle this
154 * a wrapper will have to be created.
155 */
156 final Matcher returnsNormally = const _ReturnsNormally();
157
158 class _Throws extends BaseMatcher {
159 final Matcher _matcher;
160
161 const _Throws([Matcher this._matcher = null]);
162
163 bool matches(item) {
164 try {
165 item();
166 return false;
167 } catch (final e) {
168 return _matcher == null || _matcher.matches(e);
169 }
170 }
171
172 Description describe(Description description) {
173 if (_matcher == null) {
174 return description.add("throws an exception");
175 } else {
176 return description.add('throws an exception which matches ').
177 addDescriptionOf(_matcher);
178 }
179 }
180
181 Description describeMismatch(item, Description mismatchDescription) {
182 try {
183 item();
184 return mismatchDescription.add(' no exception');
185 } catch (final e) {
186 return mismatchDescription.add(' exception does not match ').
187 addDescriptionOf(_matcher);
188 }
189 }
190 }
191
192 class _ReturnsNormally extends BaseMatcher {
193
194 const _ReturnsNormally();
195
196 bool matches(f) {
197 try {
198 f();
199 return true;
200 } catch (final e) {
201 return false;
202 }
203 }
204
205 Description describe(Description description) =>
206 description.add("return normally");
207
208 Description describeMismatch(item, Description mismatchDescription) {
209 return mismatchDescription.add(' threw exception');
210 }
211 }
212
213 /** A matcher for functions that throw BadNumberFormatException */
214 final Matcher throwsBadNumberFormatException =
215 new _Throws(new isInstanceOf<BadNumberFormatException>());
216
217 /** A matcher for functions that throw an Exception */
218 final Matcher throwsException =
219 new _Throws(new isInstanceOf<Exception>());
220
221 /** A matcher for functions that throw an IllegalArgumentException */
222 final Matcher throwsIllegalArgumentException =
223 new _Throws(new isInstanceOf<IllegalArgumentException>());
224
225 /** A matcher for functions that throw an IllegalJSRegExpException */
226 final Matcher throwsIllegalJSRegExpException =
227 new _Throws(new isInstanceOf<IllegalJSRegExpException>());
228
229 /** A matcher for functions that throw an IndexOutOfRangeException */
230 final Matcher throwsIndexOutOfRangeException =
231 new _Throws(new isInstanceOf<IndexOutOfRangeException>());
232
233 /** A matcher for functions that throw a NoSuchMethodException */
234 final Matcher throwsNoSuchMethodException =
235 new _Throws(new isInstanceOf<NoSuchMethodException>());
236
237 /** A matcher for functions that throw a NotImplementedException */
238 final Matcher throwsNotImplementedException =
239 new _Throws(new isInstanceOf<NotImplementedException>());
240
241 /** A matcher for functions that throw a NullPointerException */
242 final Matcher throwsNullPointerException =
243 new _Throws(new isInstanceOf<NullPointerException>());
244
245 /** A matcher for functions that throw an UnsupportedOperationException */
246 final Matcher throwsUnsupportedOperationException =
247 new _Throws(new isInstanceOf<UnsupportedOperationException>());
248
249 /**
250 * Returns a matcher that matches if an object has a length property
251 * that matches [matcher].
252 */
253 Matcher hasLength(matcher) =>
254 new _HasLength(wrapMatcher(matcher));
255
256 class _HasLength extends BaseMatcher {
257 final Matcher _matcher;
258 const _HasLength([Matcher this._matcher = null]);
259
260 bool matches(item) {
261 return _matcher.matches(item.length);
262 }
263
264 Description describe(Description description) =>
265 description.add('an object with length of ').
266 addDescriptionOf(_matcher);
267
268 Description describeMismatch(item, Description mismatchDescription) {
269 super.describeMismatch(item, mismatchDescription);
270 try {
271 // We want to generate a different description if there is no length
272 // property. This is harmless code that will throw if no length property
273 // but subtle enough that an optimizer shouldn't strip it out.
274 if (item.length * item.length >= 0) {
275 return mismatchDescription.add(' with length of ').
276 addDescriptionOf(item.length);
277 }
278 } catch (var e) {
279 return mismatchDescription.add(' has no length property');
280 }
281 }
282 }
283
284 /**
285 * Returns a matcher that does a deep recursive match. This only works
286 * with scalars, Maps and Iterables. To handle cyclic structures an
287 * item [limit] can be provided; if after [limit] items have been
288 * compared and the process is not complete this will be treated as
289 * a mismatch. The default limit is 1000.
290 */
291 Matcher recursivelyMatches(expected, [limit=1000]) =>
292 new _DeepMatcher(expected, limit);
293
294 // A utility function for comparing iterators
295
296 String _compareIterables(expected, actual, matcher) {
297 if (actual is !Iterable) {
298 return 'is not Iterable';
299 }
300 var expectedIterator = expected.iterator();
301 var actualIterator = actual.iterator();
302 var position = 0;
303 String reason = null;
304 while (reason == null) {
305 if (expectedIterator.hasNext()) {
306 if (actualIterator.hasNext()) {
307 reason = matcher(expectedIterator.next(),
308 actualIterator.next(),
309 'mismatch at position ${position}');
310 ++position;
311 } else {
312 reason = 'shorter than expected';
313 }
314 } else if (actualIterator.hasNext()) {
315 reason = 'longer than expected';
316 } else {
317 return null;
318 }
319 }
320 return reason;
321 }
322
323 class _DeepMatcher extends BaseMatcher {
324 final _expected;
325 final int _limit;
326 var count;
327
328 _DeepMatcher(this._expected, [this._limit = 1000]);
329
330 String _recursiveMatch(expected, actual, String location) {
331 String reason = null;
332 if (++count >= _limit) {
333 reason = 'item comparison limit exceeded';
334 } else if (expected is Iterable) {
335 reason = _compareIterables(expected, actual, _recursiveMatch);
336 } else if (expected is Map) {
337 if (actual is !Map) {
338 reason = 'expected a map';
339 } else if (expected.length != actual.length) {
340 reason = 'different map lengths';
341 } else {
342 for (var key in expected.getKeys()) {
343 if (!actual.containsKey(key)) {
344 reason = 'missing map key ${key}';
345 break;
346 }
347 reason = _recursiveMatch(expected[key], actual[key],
348 'with key ${key} ${location}');
349 if (reason != null) {
350 break;
351 }
352 }
353 }
354 } else if (expected != actual) {
355 reason = 'expected ${expected} but got ${actual}';
356 }
357 if (reason == null) {
358 return null;
359 } else {
360 return '${reason} ${location}';
361 }
362 }
363
364 String _match(expected, actual) {
365 count = 0;
366 return _recursiveMatch(expected, actual, '');
367 }
368
369 bool matches(item) => _match(_expected, item) == null;
370
371 Description describe(Description description) =>
372 description.add('recursively matches ').addDescriptionOf(_expected);
373
374 Description describeMismatch(item, Description mismatchDescription) =>
375 mismatchDescription.add(_match(_expected, item));
376 }
377
378 /**
379 * Returns a matcher that matches if the match argument contains
380 * the expected value. For [String]s this means substring matching;
381 * for [Map]s is means the map has the key, and for [Collection]s it
382 * means the collection has a matching element. In the case of collections,
383 * [expected] can itself be a matcher.
384 */
385 Matcher contains(expected) => new _Contains(expected);
386
387 class _Contains extends BaseMatcher {
388
389 final _expected;
390
391 const _Contains(this._expected);
392
393 bool matches(item) {
394 if (item is String) {
395 return item.indexOf(_expected) >= 0;
396 } else if (item is Collection) {
397 if (_expected is Matcher) {
398 return item.some((e) => _expected.matches(e));
399 } else {
400 return item.some((e) => e == _expected);
401 }
402 } else if (item is Map) {
403 return item.containsKey(_expected);
404 }
405 return false;
406 }
407
408 Description describe(Description description) =>
409 description.add('contains ').addDescriptionOf(_expected);
410 }
OLDNEW
« no previous file with comments | « lib/unittest/collection_matchers.dart ('k') | lib/unittest/description.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698