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

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

Issue 10579008: Added test setup/teardown. (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
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 5
6 /** 6 /**
7 * Returns a matcher that matches empty strings, maps or collections. 7 * Returns a matcher that matches empty strings, maps or collections.
8 */ 8 */
9 final Matcher isEmpty = const _Empty(); 9 final Matcher isEmpty = const _Empty();
10 10
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 71
72 class _IsSameAs extends BaseMatcher { 72 class _IsSameAs extends BaseMatcher {
73 final _expected; 73 final _expected;
74 const _IsSameAs(this._expected); 74 const _IsSameAs(this._expected);
75 bool matches(item) => item === _expected; 75 bool matches(item) => item === _expected;
76 // If all types were hashable we could show a hash here. 76 // If all types were hashable we could show a hash here.
77 Description describe(Description description) => 77 Description describe(Description description) =>
78 description.add('same instance as ').addDescriptionOf(_expected); 78 description.add('same instance as ').addDescriptionOf(_expected);
79 } 79 }
80 80
81 /** Returns a matcher that matches if two objects are equal (==). */ 81 /**
82 Matcher equals(expected) => new _IsEqual(expected); 82 * Returns a matcher that does a deep recursive match. This only works
83 * with scalars, Maps and Iterables. To handle cyclic structures a
84 * recursion depth [limit can be pr]ovided. The default limit is 100.
Bob Nystrom 2012/06/19 23:38:33 That ] is in a weird place.
gram 2012/06/20 17:44:14 Telekinesis
85 */
86 Matcher equals(expected, [limit=100]) =>
87 new _DeepMatcher(expected, limit);
83 88
84 class _IsEqual extends BaseMatcher { 89 class _DeepMatcher extends BaseMatcher {
85 final _expected; 90 final _expected;
86 const _IsEqual(this._expected); 91 final int _limit;
87 bool matches(item) => item == _expected; 92 var count;
93
94 _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit;
95
96 // A utility function for comparing iterators
Bob Nystrom 2012/06/19 23:38:33 This comment doesn't add a lot of value.
gram 2012/06/20 17:44:14 Removed
97
98 String _compareIterables(expected, actual, matcher, depth) {
99 if (actual is !Iterable) {
100 return 'is not Iterable';
101 }
102 var expectedIterator = expected.iterator();
103 var actualIterator = actual.iterator();
104 var position = 0;
105 String reason = null;
106 while (reason == null) {
107 if (expectedIterator.hasNext()) {
108 if (actualIterator.hasNext()) {
109 reason = matcher(expectedIterator.next(),
110 actualIterator.next(),
111 'mismatch at position ${position}',
112 depth);
113 ++position;
114 } else {
115 reason = 'shorter than expected';
116 }
117 } else if (actualIterator.hasNext()) {
118 reason = 'longer than expected';
119 } else {
120 return null;
121 }
122 }
123 return reason;
124 }
125
126 Description _recursiveMatch(expected, actual, String location, int depth) {
127 Description reason = null;
128 // If _limit is 1 we can only recurse one level into object
Bob Nystrom 2012/06/19 23:38:33 Add a "."
gram 2012/06/20 17:44:14 Done.
129 bool canRecurse = depth == 0 || _limit > 1;
130 if (expected != actual) {
Bob Nystrom 2012/06/19 23:38:33 This is some painfully nested flow control. Can yo
gram 2012/06/20 17:44:14 Done.
131 if (depth > _limit) {
132 reason = new StringDescription('recursion depth limit exceeded');
Bob Nystrom 2012/06/19 23:38:33 Extra space before "new".
gram 2012/06/20 17:44:14 Done.
133 } else {
134 if (expected is Iterable && canRecurse) {
135 String r = _compareIterables(expected, actual,
136 _recursiveMatch, depth+1);
137 if (r != null) reason = new StringDescription(r);
138 } else if (expected is Map && canRecurse) {
139 if (actual is !Map) {
140 reason = new StringDescription('expected a map');
141 } else if (expected.length != actual.length) {
142 reason = new StringDescription('different map lengths');
143 } else {
144 for (var key in expected.getKeys()) {
145 if (!actual.containsKey(key)) {
146 reason = new StringDescription('missing map key ');
147 reason.addDescriptionOf(key);
148 break;
149 }
150 reason = _recursiveMatch(expected[key], actual[key],
151 'with key <${key}> ${location}', depth+1);
152 if (reason != null) {
153 break;
154 }
155 }
156 }
157 } else {
158 // If we have recursed, show the expected value too; if not,
159 // expect() will show it for us.
160 reason = new StringDescription();
161 if (depth > 1) {
162 reason.add('expected ').addDescriptionOf(expected).add(' but was ').
163 addDescriptionOf(actual);
164 } else {
165 reason.add('was ').addDescriptionOf(actual);
166 }
167 }
168 }
169 }
170 if (reason == null) {
171 return null;
172 } else {
173 return reason.add(' ').add(location);
174 }
175 }
176
177 String _match(expected, actual) {
178 Description reason = _recursiveMatch(expected, actual, '', 0);
179 return reason == null ? null : reason.toString();
180 }
181
182 bool matches(item) => _match(_expected, item) == null;
183
88 Description describe(Description description) => 184 Description describe(Description description) =>
89 description.addDescriptionOf(_expected); 185 description.addDescriptionOf(_expected);
186
187 Description describeMismatch(item, Description mismatchDescription) =>
188 mismatchDescription.add(_match(_expected, item));
90 } 189 }
91 190
92 /** A matcher that matches any value. */ 191 /** A matcher that matches any value. */
93 final Matcher anything = const _IsAnything(); 192 final Matcher anything = const _IsAnything();
94 193
95 class _IsAnything extends BaseMatcher { 194 class _IsAnything extends BaseMatcher {
96 const _IsAnything(); 195 const _IsAnything();
97 bool matches(item) => true; 196 bool matches(item) => true;
98 Description describe(Description description) => 197 Description describe(Description description) =>
99 description.add('anything'); 198 description.add('anything');
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 } 332 }
234 333
235 Description describe(Description description) => 334 Description describe(Description description) =>
236 description.add("return normally"); 335 description.add("return normally");
237 336
238 Description describeMismatch(item, Description mismatchDescription) { 337 Description describeMismatch(item, Description mismatchDescription) {
239 return mismatchDescription.add(' threw exception'); 338 return mismatchDescription.add(' threw exception');
240 } 339 }
241 } 340 }
242 341
342 /*
343 * Matchers for different exception types. Ideally we should just be able to
344 * use something like:
345 *
346 * final Matcher throwsException =
347 * const _Throws(const isInstanceOf<Exception>());
348 *
349 * Unfortunately instanceOf is not working with dart2js.
350 *
351 * Alternatively, if static functions could be used in const expressions,
352 * we could use:
353 *
354 * bool _isException(x) => x is Exception;
355 * final Matcher isException = const _Predicate(_isException, "Exception");
356 * final Matcher throwsException = const _Throws(isException);
357 *
358 * But currently using static functions in const expressions is not supported.
359 * For now the only solution for all platforms seems to be separate classes
360 * for each exception type.
361 */
362
363 /* abstract */ class _ExceptionMatcher extends BaseMatcher {
364 final String _name;
365 const _ExceptionMatcher(this._name);
366 Description describe(Description description) =>
367 description.add(_name);
368 }
369
370 /** A matcher for BadNumberFormatExceptions. */
371 final isBadNumberFormatException = const _BadNumberFormatException();
372
243 /** A matcher for functions that throw BadNumberFormatException */ 373 /** A matcher for functions that throw BadNumberFormatException */
244 final Matcher throwsBadNumberFormatException = 374 final Matcher throwsBadNumberFormatException =
245 const _Throws(const isInstanceOf<BadNumberFormatException>()); 375 const _Throws(isBadNumberFormatException);
246 376
247 /** A matcher for functions that throw an Exception */ 377 class _BadNumberFormatException extends _ExceptionMatcher {
248 final Matcher throwsException = 378 const _BadNumberFormatException() : super("BadNumberFormatException");
249 const _Throws(const isInstanceOf<Exception>()); 379 bool matches(item) => item is BadNumberFormatException;
380 }
250 381
251 /** A matcher for functions that throw an IllegalArgumentException */ 382 /** A matcher for Exceptions. */
383 final isException = const _Exception();
384
385 /** A matcher for functions that throw Exception */
386 final Matcher throwsException = const _Throws(isException);
387
388 class _Exception extends _ExceptionMatcher {
389 const _Exception() : super("Exception");
390 bool matches(item) => item is Exception;
391 }
392
393 /** A matcher for IllegalArgumentExceptions. */
394 final isIllegalArgumentException = const _IllegalArgumentException();
395
396 /** A matcher for functions that throw IllegalArgumentException */
252 final Matcher throwsIllegalArgumentException = 397 final Matcher throwsIllegalArgumentException =
253 const _Throws(const isInstanceOf<IllegalArgumentException>()); 398 const _Throws(isIllegalArgumentException);
254 399
255 /** A matcher for functions that throw an IllegalJSRegExpException */ 400 class _IllegalArgumentException extends _ExceptionMatcher {
401 const _IllegalArgumentException() : super("IllegalArgumentException");
402 bool matches(item) => item is IllegalArgumentException;
403 }
404
405 /** A matcher for IllegalJSRegExpExceptions. */
406 final isIllegalJSRegExpException = const _IllegalJSRegExpException();
407
408 /** A matcher for functions that throw IllegalJSRegExpException */
256 final Matcher throwsIllegalJSRegExpException = 409 final Matcher throwsIllegalJSRegExpException =
257 const _Throws(const isInstanceOf<IllegalJSRegExpException>()); 410 const _Throws(isIllegalJSRegExpException);
258 411
259 /** A matcher for functions that throw an IndexOutOfRangeException */ 412 class _IllegalJSRegExpException extends _ExceptionMatcher {
413 const _IllegalJSRegExpException() : super("IllegalJSRegExpException");
414 bool matches(item) => item is IllegalJSRegExpException;
415 }
416
417 /** A matcher for IndexOutOfRangeExceptions. */
418 final isIndexOutOfRangeException = const _IndexOutOfRangeException();
419
420 /** A matcher for functions that throw IndexOutOfRangeException */
260 final Matcher throwsIndexOutOfRangeException = 421 final Matcher throwsIndexOutOfRangeException =
261 const _Throws(const isInstanceOf<IndexOutOfRangeException>()); 422 const _Throws(isIndexOutOfRangeException);
262 423
263 /** A matcher for functions that throw a NoSuchMethodException */ 424 class _IndexOutOfRangeException extends _ExceptionMatcher {
425 const _IndexOutOfRangeException() : super("IndexOutOfRangeException");
426 bool matches(item) => item is IndexOutOfRangeException;
427 }
428
429 /** A matcher for NoSuchMethodExceptions. */
430 final isNoSuchMethodException = const _NoSuchMethodException();
431
432 /** A matcher for functions that throw NoSuchMethodException */
264 final Matcher throwsNoSuchMethodException = 433 final Matcher throwsNoSuchMethodException =
265 const _Throws(const isInstanceOf<NoSuchMethodException>()); 434 const _Throws(isNoSuchMethodException);
266 435
267 /** A matcher for functions that throw a NotImplementedException */ 436 class _NoSuchMethodException extends _ExceptionMatcher {
437 const _NoSuchMethodException() : super("NoSuchMethodException");
438 bool matches(item) => item is NoSuchMethodException;
439 }
440
441 /** A matcher for NotImplementedExceptions. */
442 final isNotImplementedException = const _NotImplementedException();
443
444 /** A matcher for functions that throw Exception */
268 final Matcher throwsNotImplementedException = 445 final Matcher throwsNotImplementedException =
269 const _Throws(const isInstanceOf<NotImplementedException>()); 446 const _Throws(isNotImplementedException);
270 447
271 /** A matcher for functions that throw a NullPointerException */ 448 class _NotImplementedException extends _ExceptionMatcher {
449 const _NotImplementedException() : super("NotImplementedException");
450 bool matches(item) => item is NotImplementedException;
451 }
452
453 /** A matcher for NullPointerExceptions. */
454 final isNullPointerException = const _NullPointerException();
455
456 /** A matcher for functions that throw NotNullPointerException */
272 final Matcher throwsNullPointerException = 457 final Matcher throwsNullPointerException =
273 const _Throws(const isInstanceOf<NullPointerException>()); 458 const _Throws(isNullPointerException);
274 459
275 /** A matcher for functions that throw an UnsupportedOperationException */ 460 class _NullPointerException extends _ExceptionMatcher {
461 const _NullPointerException() : super("NullPointerException");
462 bool matches(item) => item is NullPointerException;
463 }
464
465 /** A matcher for UnsupportedOperationExceptions. */
466 final isUnsupportedOperationException = const _UnsupportedOperationException();
467
468 /** A matcher for functions that throw UnsupportedOperationException */
276 final Matcher throwsUnsupportedOperationException = 469 final Matcher throwsUnsupportedOperationException =
277 const _Throws(const isInstanceOf<UnsupportedOperationException>()); 470 const _Throws(isUnsupportedOperationException);
471
472 class _UnsupportedOperationException extends _ExceptionMatcher {
473 const _UnsupportedOperationException() :
474 super("UnsupportedOperationException");
475 bool matches(item) => item is UnsupportedOperationException;
476 }
278 477
279 /** 478 /**
280 * Returns a matcher that matches if an object has a length property 479 * Returns a matcher that matches if an object has a length property
281 * that matches [matcher]. 480 * that matches [matcher].
282 */ 481 */
283 Matcher hasLength(matcher) => 482 Matcher hasLength(matcher) =>
284 new _HasLength(wrapMatcher(matcher)); 483 new _HasLength(wrapMatcher(matcher));
285 484
286 class _HasLength extends BaseMatcher { 485 class _HasLength extends BaseMatcher {
287 final Matcher _matcher; 486 final Matcher _matcher;
(...skipping 17 matching lines...) Expand all
305 return mismatchDescription.add(' with length of '). 504 return mismatchDescription.add(' with length of ').
306 addDescriptionOf(item.length); 505 addDescriptionOf(item.length);
307 } 506 }
308 } catch (var e) { 507 } catch (var e) {
309 return mismatchDescription.add(' has no length property'); 508 return mismatchDescription.add(' has no length property');
310 } 509 }
311 } 510 }
312 } 511 }
313 512
314 /** 513 /**
315 * Returns a matcher that does a deep recursive match. This only works
316 * with scalars, Maps and Iterables. To handle cyclic structures an
317 * item [limit] can be provided; if after [limit] items have been
318 * compared and the process is not complete this will be treated as
319 * a mismatch. The default limit is 1000.
320 */
321 Matcher recursivelyMatches(expected, [limit=1000]) =>
322 new _DeepMatcher(expected, limit);
323
324 // A utility function for comparing iterators
325
326 String _compareIterables(expected, actual, matcher) {
327 if (actual is !Iterable) {
328 return 'is not Iterable';
329 }
330 var expectedIterator = expected.iterator();
331 var actualIterator = actual.iterator();
332 var position = 0;
333 String reason = null;
334 while (reason == null) {
335 if (expectedIterator.hasNext()) {
336 if (actualIterator.hasNext()) {
337 reason = matcher(expectedIterator.next(),
338 actualIterator.next(),
339 'mismatch at position ${position}');
340 ++position;
341 } else {
342 reason = 'shorter than expected';
343 }
344 } else if (actualIterator.hasNext()) {
345 reason = 'longer than expected';
346 } else {
347 return null;
348 }
349 }
350 return reason;
351 }
352
353 class _DeepMatcher extends BaseMatcher {
354 final _expected;
355 final int _limit;
356 var count;
357
358 _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit;
359
360 String _recursiveMatch(expected, actual, String location) {
361 String reason = null;
362 if (++count >= _limit) {
363 reason = 'item comparison limit exceeded';
364 } else if (expected is Iterable) {
365 reason = _compareIterables(expected, actual, _recursiveMatch);
366 } else if (expected is Map) {
367 if (actual is !Map) {
368 reason = 'expected a map';
369 } else if (expected.length != actual.length) {
370 reason = 'different map lengths';
371 } else {
372 for (var key in expected.getKeys()) {
373 if (!actual.containsKey(key)) {
374 reason = 'missing map key ${key}';
375 break;
376 }
377 reason = _recursiveMatch(expected[key], actual[key],
378 'with key ${key} ${location}');
379 if (reason != null) {
380 break;
381 }
382 }
383 }
384 } else if (expected != actual) {
385 reason = 'expected ${expected} but got ${actual}';
386 }
387 if (reason == null) {
388 return null;
389 } else {
390 return '${reason} ${location}';
391 }
392 }
393
394 String _match(expected, actual) {
395 count = 0;
396 return _recursiveMatch(expected, actual, '');
397 }
398
399 bool matches(item) => _match(_expected, item) == null;
400
401 Description describe(Description description) =>
402 description.add('recursively matches ').addDescriptionOf(_expected);
403
404 Description describeMismatch(item, Description mismatchDescription) =>
405 mismatchDescription.add(_match(_expected, item));
406 }
407
408 /**
409 * Returns a matcher that matches if the match argument contains 514 * Returns a matcher that matches if the match argument contains
410 * the expected value. For [String]s this means substring matching; 515 * the expected value. For [String]s this means substring matching;
411 * for [Map]s is means the map has the key, and for [Collection]s it 516 * for [Map]s is means the map has the key, and for [Collection]s it
412 * means the collection has a matching element. In the case of collections, 517 * means the collection has a matching element. In the case of collections,
413 * [expected] can itself be a matcher. 518 * [expected] can itself be a matcher.
414 */ 519 */
415 Matcher contains(expected) => new _Contains(expected); 520 Matcher contains(expected) => new _Contains(expected);
416 521
417 class _Contains extends BaseMatcher { 522 class _Contains extends BaseMatcher {
418 523
(...skipping 13 matching lines...) Expand all
432 } else if (item is Map) { 537 } else if (item is Map) {
433 return item.containsKey(_expected); 538 return item.containsKey(_expected);
434 } 539 }
435 return false; 540 return false;
436 } 541 }
437 542
438 Description describe(Description description) => 543 Description describe(Description description) =>
439 description.add('contains ').addDescriptionOf(_expected); 544 description.add('contains ').addDescriptionOf(_expected);
440 } 545 }
441 546
547 /**
548 * Returns a matcher that matches if the match argument is in
549 * the expected value. This is the converse of [contains].
550 */
551 Matcher isIn(expected) => new _In(expected);
552
Sean Eagan 2012/06/20 18:00:22 Thanks for adding this! Just a small gripe, I thi
553 class _In extends BaseMatcher {
554
555 final _expected;
556
557 const _In(this._expected);
558
559 bool matches(item) {
560 if (_expected is String) {
561 return _expected.indexOf(item) >= 0;
562 } else if (_expected is Collection) {
563 return _expected.some((e) => e == item);
564 } else if (_expected is Map) {
565 return _expected.containsKey(item);
566 }
567 return false;
568 }
569
570 Description describe(Description description) =>
571 description.add('is in ').addDescriptionOf(_expected);
572 }
573
574 /**
575 * Returns a matcher that uses an arbitrary function that returns
576 * true or false for the actual value.
577 */
578 Matcher predicate(f, [description = 'satisfies function']) =>
579 new _Predicate(f, description);
580
581 class _Predicate extends BaseMatcher {
582
583 final _matcher;
584 final String _description;
585
586 const _Predicate(this._matcher, this._description);
587
588 bool matches(item) => _matcher(item);
589
590 Description describe(Description description) =>
591 description.add(_description);
592 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698