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 library matcher.core_matchers; | 5 library matcher.core_matchers; |
6 | 6 |
7 import 'dart:async'; | |
8 | |
9 import 'description.dart'; | 7 import 'description.dart'; |
10 import 'expect.dart'; | |
11 import 'interfaces.dart'; | 8 import 'interfaces.dart'; |
| 9 import 'util.dart'; |
12 | 10 |
13 /// Returns a matcher that matches empty strings, maps or iterables | 11 /// Returns a matcher that matches empty strings, maps or iterables |
14 /// (including collections). | 12 /// (including collections). |
15 const Matcher isEmpty = const _Empty(); | 13 const Matcher isEmpty = const _Empty(); |
16 | 14 |
17 class _Empty extends Matcher { | 15 class _Empty extends Matcher { |
18 const _Empty(); | 16 const _Empty(); |
19 bool matches(item, Map matchState) { | 17 bool matches(item, Map matchState) { |
20 if (item is Map || item is Iterable) { | 18 if (item is Map || item is Iterable) { |
21 return item.isEmpty; | 19 return item.isEmpty; |
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 /// parameters. | 381 /// parameters. |
384 class isInstanceOf<T> extends Matcher { | 382 class isInstanceOf<T> extends Matcher { |
385 final String _name; | 383 final String _name; |
386 const isInstanceOf([name = 'specified type']) : this._name = name; | 384 const isInstanceOf([name = 'specified type']) : this._name = name; |
387 bool matches(obj, Map matchState) => obj is T; | 385 bool matches(obj, Map matchState) => obj is T; |
388 // The description here is lame :-( | 386 // The description here is lame :-( |
389 Description describe(Description description) => | 387 Description describe(Description description) => |
390 description.add('an instance of ${_name}'); | 388 description.add('an instance of ${_name}'); |
391 } | 389 } |
392 | 390 |
393 /// This can be used to match two kinds of objects: | |
394 /// | |
395 /// * A [Function] that throws an exception when called. The function cannot | |
396 /// take any arguments. If you want to test that a function expecting | |
397 /// arguments throws, wrap it in another zero-argument function that calls | |
398 /// the one you want to test. | |
399 /// | |
400 /// * A [Future] that completes with an exception. Note that this creates an | |
401 /// asynchronous expectation. The call to `expect()` that includes this will | |
402 /// return immediately and execution will continue. Later, when the future | |
403 /// completes, the actual expectation will run. | |
404 const Matcher throws = const Throws(); | |
405 | |
406 /// This can be used to match two kinds of objects: | |
407 /// | |
408 /// * A [Function] that throws an exception when called. The function cannot | |
409 /// take any arguments. If you want to test that a function expecting | |
410 /// arguments throws, wrap it in another zero-argument function that calls | |
411 /// the one you want to test. | |
412 /// | |
413 /// * A [Future] that completes with an exception. Note that this creates an | |
414 /// asynchronous expectation. The call to `expect()` that includes this will | |
415 /// return immediately and execution will continue. Later, when the future | |
416 /// completes, the actual expectation will run. | |
417 /// | |
418 /// In both cases, when an exception is thrown, this will test that the exceptio
n | |
419 /// object matches [matcher]. If [matcher] is not an instance of [Matcher], it | |
420 /// will implicitly be treated as `equals(matcher)`. | |
421 Matcher throwsA(matcher) => new Throws(wrapMatcher(matcher)); | |
422 | |
423 /// A matcher that matches a function call against no exception. | 391 /// A matcher that matches a function call against no exception. |
424 /// The function will be called once. Any exceptions will be silently swallowed. | 392 /// The function will be called once. Any exceptions will be silently swallowed. |
425 /// The value passed to expect() should be a reference to the function. | 393 /// The value passed to expect() should be a reference to the function. |
426 /// Note that the function cannot take arguments; to handle this | 394 /// Note that the function cannot take arguments; to handle this |
427 /// a wrapper will have to be created. | 395 /// a wrapper will have to be created. |
428 const Matcher returnsNormally = const _ReturnsNormally(); | 396 const Matcher returnsNormally = const _ReturnsNormally(); |
429 | 397 |
430 class Throws extends Matcher { | |
431 final Matcher _matcher; | |
432 | |
433 const Throws([Matcher matcher]) : this._matcher = matcher; | |
434 | |
435 bool matches(item, Map matchState) { | |
436 if (item is! Function && item is! Future) return false; | |
437 if (item is Future) { | |
438 var done = wrapAsync((fn) => fn()); | |
439 | |
440 // Queue up an asynchronous expectation that validates when the future | |
441 // completes. | |
442 item.then((value) { | |
443 done(() { | |
444 fail("Expected future to fail, but succeeded with '$value'."); | |
445 }); | |
446 }, onError: (error, trace) { | |
447 done(() { | |
448 if (_matcher == null) return; | |
449 var reason; | |
450 if (trace != null) { | |
451 var stackTrace = trace.toString(); | |
452 stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}"; | |
453 reason = "Actual exception trace:\n$stackTrace"; | |
454 } | |
455 expect(error, _matcher, reason: reason); | |
456 }); | |
457 }); | |
458 // It hasn't failed yet. | |
459 return true; | |
460 } | |
461 | |
462 try { | |
463 item(); | |
464 return false; | |
465 } catch (e, s) { | |
466 if (_matcher == null || _matcher.matches(e, matchState)) { | |
467 return true; | |
468 } else { | |
469 addStateInfo(matchState, {'exception': e, 'stack': s}); | |
470 return false; | |
471 } | |
472 } | |
473 } | |
474 | |
475 Description describe(Description description) { | |
476 if (_matcher == null) { | |
477 return description.add("throws"); | |
478 } else { | |
479 return description.add('throws ').addDescriptionOf(_matcher); | |
480 } | |
481 } | |
482 | |
483 Description describeMismatch(item, Description mismatchDescription, | |
484 Map matchState, | |
485 bool verbose) { | |
486 if (item is! Function && item is! Future) { | |
487 return mismatchDescription.add('is not a Function or Future'); | |
488 } else if (_matcher == null || matchState['exception'] == null) { | |
489 return mismatchDescription.add('did not throw'); | |
490 } else { | |
491 mismatchDescription. add('threw '). | |
492 addDescriptionOf(matchState['exception']); | |
493 if (verbose) { | |
494 mismatchDescription.add(' at ').add(matchState['stack'].toString()); | |
495 } | |
496 return mismatchDescription; | |
497 } | |
498 } | |
499 } | |
500 | |
501 class _ReturnsNormally extends Matcher { | 398 class _ReturnsNormally extends Matcher { |
502 const _ReturnsNormally(); | 399 const _ReturnsNormally(); |
503 | 400 |
504 bool matches(f, Map matchState) { | 401 bool matches(f, Map matchState) { |
505 try { | 402 try { |
506 f(); | 403 f(); |
507 return true; | 404 return true; |
508 } catch (e, s) { | 405 } catch (e, s) { |
509 addStateInfo(matchState, {'exception': e, 'stack': s}); | 406 addStateInfo(matchState, {'exception': e, 'stack': s}); |
510 return false; | 407 return false; |
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
738 addDescriptionOf(matchState['feature']); | 635 addDescriptionOf(matchState['feature']); |
739 var innerDescription = new StringDescription(); | 636 var innerDescription = new StringDescription(); |
740 _matcher.describeMismatch(matchState['feature'], innerDescription, | 637 _matcher.describeMismatch(matchState['feature'], innerDescription, |
741 matchState['state'], verbose); | 638 matchState['state'], verbose); |
742 if (innerDescription.length > 0) { | 639 if (innerDescription.length > 0) { |
743 mismatchDescription.add(' which ').add(innerDescription.toString()); | 640 mismatchDescription.add(' which ').add(innerDescription.toString()); |
744 } | 641 } |
745 return mismatchDescription; | 642 return mismatchDescription; |
746 } | 643 } |
747 } | 644 } |
OLD | NEW |