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

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

Issue 313563002: pkg/matcher: Reverting 36881,36896 while investigating dart2js checked crash (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 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 | « pkg/matcher/lib/mirror_matchers.dart ('k') | pkg/matcher/lib/src/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
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 part of matcher;
6 6
7 import 'dart:async'; 7 /**
8 8 * Returns a matcher that matches empty strings, maps or iterables
9 import 'description.dart'; 9 * (including collections).
10 import 'expect.dart'; 10 */
11 import 'interfaces.dart';
12
13 /// Returns a matcher that matches empty strings, maps or iterables
14 /// (including collections).
15 const Matcher isEmpty = const _Empty(); 11 const Matcher isEmpty = const _Empty();
16 12
17 class _Empty extends Matcher { 13 class _Empty extends Matcher {
18 const _Empty(); 14 const _Empty();
19 bool matches(item, Map matchState) { 15 bool matches(item, Map matchState) {
20 if (item is Map || item is Iterable) { 16 if (item is Map || item is Iterable) {
21 return item.isEmpty; 17 return item.isEmpty;
22 } else if (item is String) { 18 } else if (item is String) {
23 return item.length == 0; 19 return item.length == 0;
24 } else { 20 } else {
25 return false; 21 return false;
26 } 22 }
27 } 23 }
28 Description describe(Description description) => description.add('empty'); 24 Description describe(Description description) => description.add('empty');
29 } 25 }
30 26
31 /// A matcher that matches any null value. 27 /** A matcher that matches any null value. */
32 const Matcher isNull = const _IsNull(); 28 const Matcher isNull = const _IsNull();
33 29
34 /// A matcher that matches any non-null value. 30 /** A matcher that matches any non-null value. */
35 const Matcher isNotNull = const _IsNotNull(); 31 const Matcher isNotNull = const _IsNotNull();
36 32
37 class _IsNull extends Matcher { 33 class _IsNull extends Matcher {
38 const _IsNull(); 34 const _IsNull();
39 bool matches(item, Map matchState) => item == null; 35 bool matches(item, Map matchState) => item == null;
40 Description describe(Description description) => description.add('null'); 36 Description describe(Description description) => description.add('null');
41 } 37 }
42 38
43 class _IsNotNull extends Matcher { 39 class _IsNotNull extends Matcher {
44 const _IsNotNull(); 40 const _IsNotNull();
45 bool matches(item, Map matchState) => item != null; 41 bool matches(item, Map matchState) => item != null;
46 Description describe(Description description) => description.add('not null'); 42 Description describe(Description description) => description.add('not null');
47 } 43 }
48 44
49 /// A matcher that matches the Boolean value true. 45 /** A matcher that matches the Boolean value true. */
50 const Matcher isTrue = const _IsTrue(); 46 const Matcher isTrue = const _IsTrue();
51 47
52 /// A matcher that matches anything except the Boolean value true. 48 /** A matcher that matches anything except the Boolean value true. */
53 const Matcher isFalse = const _IsFalse(); 49 const Matcher isFalse = const _IsFalse();
54 50
55 class _IsTrue extends Matcher { 51 class _IsTrue extends Matcher {
56 const _IsTrue(); 52 const _IsTrue();
57 bool matches(item, Map matchState) => item == true; 53 bool matches(item, Map matchState) => item == true;
58 Description describe(Description description) => description.add('true'); 54 Description describe(Description description) => description.add('true');
59 } 55 }
60 56
61 class _IsFalse extends Matcher { 57 class _IsFalse extends Matcher {
62 const _IsFalse(); 58 const _IsFalse();
63 bool matches(item, Map matchState) => item == false; 59 bool matches(item, Map matchState) => item == false;
64 Description describe(Description description) => description.add('false'); 60 Description describe(Description description) => description.add('false');
65 } 61 }
66 62
67 /// Returns a matches that matches if the value is the same instance 63 /**
68 /// as [expected], using [identical]. 64 * Returns a matches that matches if the value is the same instance
65 * as [expected], using [identical].
66 */
69 Matcher same(expected) => new _IsSameAs(expected); 67 Matcher same(expected) => new _IsSameAs(expected);
70 68
71 class _IsSameAs extends Matcher { 69 class _IsSameAs extends Matcher {
72 final _expected; 70 final _expected;
73 const _IsSameAs(this._expected); 71 const _IsSameAs(this._expected);
74 bool matches(item, Map matchState) => identical(item, _expected); 72 bool matches(item, Map matchState) => identical(item, _expected);
75 // If all types were hashable we could show a hash here. 73 // If all types were hashable we could show a hash here.
76 Description describe(Description description) => 74 Description describe(Description description) =>
77 description.add('same instance as ').addDescriptionOf(_expected); 75 description.add('same instance as ').addDescriptionOf(_expected);
78 } 76 }
79 77
80 /// Returns a matcher that matches if the value is structurally equal to 78 /**
81 /// [expected]. 79 * Returns a matcher that matches if the value is structurally equal to
82 /// 80 * [expected].
83 /// If [expected] is a [Matcher], then it matches using that. Otherwise it tests 81 *
84 /// for equality using `==` on the expected value. 82 * If [expected] is a [Matcher], then it matches using that. Otherwise it tests
85 /// 83 * for equality using `==` on the expected value.
86 /// For [Iterable]s and [Map]s, this will recursively match the elements. To 84 *
87 /// handle cyclic structures a recursion depth [limit] can be provided. The 85 * For [Iterable]s and [Map]s, this will recursively match the elements. To
88 /// default limit is 100. 86 * handle cyclic structures a recursion depth [limit] can be provided. The
89 Matcher equals(expected, [int limit=100]) => 87 * default limit is 100.
88 */
89 Matcher equals(expected, [limit=100]) =>
90 expected is String 90 expected is String
91 ? new _StringEqualsMatcher(expected) 91 ? new _StringEqualsMatcher(expected)
92 : new _DeepMatcher(expected, limit); 92 : new _DeepMatcher(expected, limit);
93 93
94 class _DeepMatcher extends Matcher { 94 class _DeepMatcher extends Matcher {
95 final _expected; 95 final _expected;
96 final int _limit; 96 final int _limit;
97 var count; 97 var count;
98 98
99 _DeepMatcher(this._expected, [int limit = 1000]) : this._limit = limit; 99 _DeepMatcher(this._expected, [limit = 1000]): this._limit = limit;
100 100
101 // Returns a pair (reason, location) 101 // Returns a pair (reason, location)
102 List _compareIterables(expected, actual, matcher, depth, location) { 102 List _compareIterables(expected, actual, matcher, depth, location) {
103 if (actual is! Iterable) return ['is not Iterable', location]; 103 if (actual is! Iterable) return ['is not Iterable', location];
104 104
105 var expectedIterator = expected.iterator; 105 var expectedIterator = expected.iterator;
106 var actualIterator = actual.iterator; 106 var actualIterator = actual.iterator;
107 for (var index = 0; ; index++) { 107 for (var index = 0; ; index++) {
108 // Advance in lockstep. 108 // Advance in lockstep.
109 var expectedNext = expectedIterator.moveNext(); 109 var expectedNext = expectedIterator.moveNext();
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 // description). 228 // description).
229 if (reason.length == 0 && mismatchDescription.length > 0) { 229 if (reason.length == 0 && mismatchDescription.length > 0) {
230 mismatchDescription.add('is ').addDescriptionOf(item); 230 mismatchDescription.add('is ').addDescriptionOf(item);
231 } else { 231 } else {
232 mismatchDescription.add(reason); 232 mismatchDescription.add(reason);
233 } 233 }
234 return mismatchDescription; 234 return mismatchDescription;
235 } 235 }
236 } 236 }
237 237
238 /// A special equality matcher for strings. 238 /** A special equality matcher for strings. */
239 class _StringEqualsMatcher extends Matcher { 239 class _StringEqualsMatcher extends Matcher {
240 final String _value; 240 final String _value;
241 241
242 _StringEqualsMatcher(this._value); 242 _StringEqualsMatcher(this._value);
243 243
244 bool get showActualValue => true; 244 bool get showActualValue => true;
245 245
246 bool matches(item, Map matchState) => _value == item; 246 bool matches(item, Map matchState) => _value == item;
247 247
248 Description describe(Description description) => 248 Description describe(Description description) =>
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
306 static void _writeTrailing(StringBuffer buff, String s, int start) { 306 static void _writeTrailing(StringBuffer buff, String s, int start) {
307 if (start + 10 > s.length) { 307 if (start + 10 > s.length) {
308 buff.write(s.substring(start)); 308 buff.write(s.substring(start));
309 } else { 309 } else {
310 buff.write(s.substring(start, start + 10)); 310 buff.write(s.substring(start, start + 10));
311 buff.write(' ...'); 311 buff.write(' ...');
312 } 312 }
313 } 313 }
314 } 314 }
315 315
316 /// A matcher that matches any value. 316 /** A matcher that matches any value. */
317 const Matcher anything = const _IsAnything(); 317 const Matcher anything = const _IsAnything();
318 318
319 class _IsAnything extends Matcher { 319 class _IsAnything extends Matcher {
320 const _IsAnything(); 320 const _IsAnything();
321 bool matches(item, Map matchState) => true; 321 bool matches(item, Map matchState) => true;
322 Description describe(Description description) => description.add('anything'); 322 Description describe(Description description) => description.add('anything');
323 } 323 }
324 324
325 /// Returns a matcher that matches if an object is an instance 325 /**
326 /// of [type] (or a subtype). 326 * Returns a matcher that matches if an object is an instance
327 /// 327 * of [type] (or a subtype).
328 /// As types are not first class objects in Dart we can only 328 *
329 /// approximate this test by using a generic wrapper class. 329 * As types are not first class objects in Dart we can only
330 /// 330 * approximate this test by using a generic wrapper class.
331 /// For example, to test whether 'bar' is an instance of type 331 *
332 /// 'Foo', we would write: 332 * For example, to test whether 'bar' is an instance of type
333 /// 333 * 'Foo', we would write:
334 /// expect(bar, new isInstanceOf<Foo>()); 334 *
335 /// 335 * expect(bar, new isInstanceOf<Foo>());
336 /// To get better error message, supply a name when creating the 336 *
337 /// Type wrapper; e.g.: 337 * To get better error message, supply a name when creating the
338 /// 338 * Type wrapper; e.g.:
339 /// expect(bar, new isInstanceOf<Foo>('Foo')); 339 *
340 /// 340 * expect(bar, new isInstanceOf<Foo>('Foo'));
341 /// Note that this does not currently work in dart2js; it will 341 *
342 /// match any type, and isNot(new isInstanceof<T>()) will always 342 * Note that this does not currently work in dart2js; it will
343 /// fail. This is because dart2js currently ignores template type 343 * match any type, and isNot(new isInstanceof<T>()) will always
344 /// parameters. 344 * fail. This is because dart2js currently ignores template type
345 * parameters.
346 */
345 class isInstanceOf<T> extends Matcher { 347 class isInstanceOf<T> extends Matcher {
346 final String _name; 348 final String _name;
347 const isInstanceOf([name = 'specified type']) : this._name = name; 349 const isInstanceOf([name = 'specified type']): this._name = name;
348 bool matches(obj, Map matchState) => obj is T; 350 bool matches(obj, Map matchState) => obj is T;
349 // The description here is lame :-( 351 // The description here is lame :-(
350 Description describe(Description description) => 352 Description describe(Description description) =>
351 description.add('an instance of ${_name}'); 353 description.add('an instance of ${_name}');
352 } 354 }
353 355
354 /// This can be used to match two kinds of objects: 356 /**
355 /// 357 * This can be used to match two kinds of objects:
356 /// * A [Function] that throws an exception when called. The function cannot 358 *
357 /// take any arguments. If you want to test that a function expecting 359 * * A [Function] that throws an exception when called. The function cannot
358 /// arguments throws, wrap it in another zero-argument function that calls 360 * take any arguments. If you want to test that a function expecting
359 /// the one you want to test. 361 * arguments throws, wrap it in another zero-argument function that calls
360 /// 362 * the one you want to test.
361 /// * A [Future] that completes with an exception. Note that this creates an 363 *
362 /// asynchronous expectation. The call to `expect()` that includes this will 364 * * A [Future] that completes with an exception. Note that this creates an
363 /// return immediately and execution will continue. Later, when the future 365 * asynchronous expectation. The call to `expect()` that includes this will
364 /// completes, the actual expectation will run. 366 * return immediately and execution will continue. Later, when the future
367 * completes, the actual expectation will run.
368 */
365 const Matcher throws = const Throws(); 369 const Matcher throws = const Throws();
366 370
367 /// This can be used to match two kinds of objects: 371 /**
368 /// 372 * This can be used to match two kinds of objects:
369 /// * A [Function] that throws an exception when called. The function cannot 373 *
370 /// take any arguments. If you want to test that a function expecting 374 * * A [Function] that throws an exception when called. The function cannot
371 /// arguments throws, wrap it in another zero-argument function that calls 375 * take any arguments. If you want to test that a function expecting
372 /// the one you want to test. 376 * arguments throws, wrap it in another zero-argument function that calls
373 /// 377 * the one you want to test.
374 /// * A [Future] that completes with an exception. Note that this creates an 378 *
375 /// asynchronous expectation. The call to `expect()` that includes this will 379 * * A [Future] that completes with an exception. Note that this creates an
376 /// return immediately and execution will continue. Later, when the future 380 * asynchronous expectation. The call to `expect()` that includes this will
377 /// completes, the actual expectation will run. 381 * return immediately and execution will continue. Later, when the future
378 /// 382 * completes, the actual expectation will run.
379 /// In both cases, when an exception is thrown, this will test that the exceptio n 383 *
380 /// object matches [matcher]. If [matcher] is not an instance of [Matcher], it 384 * In both cases, when an exception is thrown, this will test that the exception
381 /// will implicitly be treated as `equals(matcher)`. 385 * object matches [matcher]. If [matcher] is not an instance of [Matcher], it
386 * will implicitly be treated as `equals(matcher)`.
387 */
382 Matcher throwsA(matcher) => new Throws(wrapMatcher(matcher)); 388 Matcher throwsA(matcher) => new Throws(wrapMatcher(matcher));
383 389
384 /// A matcher that matches a function call against no exception. 390 /**
385 /// The function will be called once. Any exceptions will be silently swallowed. 391 * A matcher that matches a function call against no exception.
386 /// The value passed to expect() should be a reference to the function. 392 * The function will be called once. Any exceptions will be silently swallowed.
387 /// Note that the function cannot take arguments; to handle this 393 * The value passed to expect() should be a reference to the function.
388 /// a wrapper will have to be created. 394 * Note that the function cannot take arguments; to handle this
395 * a wrapper will have to be created.
396 */
389 const Matcher returnsNormally = const _ReturnsNormally(); 397 const Matcher returnsNormally = const _ReturnsNormally();
390 398
391 class Throws extends Matcher { 399 class Throws extends Matcher {
392 final Matcher _matcher; 400 final Matcher _matcher;
393 401
394 const Throws([Matcher matcher]) : this._matcher = matcher; 402 const Throws([Matcher matcher]): this._matcher = matcher;
395 403
396 bool matches(item, Map matchState) { 404 bool matches(item, Map matchState) {
397 if (item is! Function && item is! Future) return false; 405 if (item is! Function && item is! Future) return false;
398 if (item is Future) { 406 if (item is Future) {
399 var done = wrapAsync((fn) => fn()); 407 var done = wrapAsync((fn) => fn());
400 408
401 // Queue up an asynchronous expectation that validates when the future 409 // Queue up an asynchronous expectation that validates when the future
402 // completes. 410 // completes.
403 item.then((value) { 411 item.then((value) {
404 done(() { 412 done(() => fail("Expected future to fail, but succeeded with '$value'.") );
405 fail("Expected future to fail, but succeeded with '$value'.");
406 });
407 }, onError: (error, trace) { 413 }, onError: (error, trace) {
408 done(() { 414 done(() {
409 if (_matcher == null) return; 415 if (_matcher == null) return;
410 var reason; 416 var reason;
411 if (trace != null) { 417 if (trace != null) {
412 var stackTrace = trace.toString(); 418 var stackTrace = trace.toString();
413 stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}"; 419 stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}";
414 reason = "Actual exception trace:\n$stackTrace"; 420 reason = "Actual exception trace:\n$stackTrace";
415 } 421 }
416 expect(error, _matcher, reason: reason); 422 expect(error, _matcher, reason: reason);
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
506 * For now the only solution for all platforms seems to be separate classes 512 * For now the only solution for all platforms seems to be separate classes
507 * for each exception type. 513 * for each exception type.
508 */ 514 */
509 515
510 abstract class TypeMatcher extends Matcher { 516 abstract class TypeMatcher extends Matcher {
511 final String _name; 517 final String _name;
512 const TypeMatcher(this._name); 518 const TypeMatcher(this._name);
513 Description describe(Description description) => description.add(_name); 519 Description describe(Description description) => description.add(_name);
514 } 520 }
515 521
516 /// A matcher for Map types. 522 /** A matcher for FormatExceptions. */
517 const Matcher isMap = const _IsMap(); 523 const isFormatException = const _FormatException();
524
525 /** A matcher for functions that throw FormatException. */
526 const Matcher throwsFormatException = const Throws(isFormatException);
527
528 class _FormatException extends TypeMatcher {
529 const _FormatException(): super("FormatException");
530 bool matches(item, Map matchState) => item is FormatException;
531 }
532
533 /** A matcher for Exceptions. */
534 const isException = const _Exception();
535
536 /** A matcher for functions that throw Exception. */
537 const Matcher throwsException = const Throws(isException);
538
539 class _Exception extends TypeMatcher {
540 const _Exception(): super("Exception");
541 bool matches(item, Map matchState) => item is Exception;
542 }
543
544 /** A matcher for ArgumentErrors. */
545 const isArgumentError = const _ArgumentError();
546
547 /** A matcher for functions that throw ArgumentError. */
548 const Matcher throwsArgumentError = const Throws(isArgumentError);
549
550 class _ArgumentError extends TypeMatcher {
551 const _ArgumentError(): super("ArgumentError");
552 bool matches(item, Map matchState) => item is ArgumentError;
553 }
554
555 /** A matcher for RangeErrors. */
556 const isRangeError = const _RangeError();
557
558 /** A matcher for functions that throw RangeError. */
559 const Matcher throwsRangeError = const Throws(isRangeError);
560
561 class _RangeError extends TypeMatcher {
562 const _RangeError(): super("RangeError");
563 bool matches(item, Map matchState) => item is RangeError;
564 }
565
566 /** A matcher for NoSuchMethodErrors. */
567 const isNoSuchMethodError = const _NoSuchMethodError();
568
569 /** A matcher for functions that throw NoSuchMethodError. */
570 const Matcher throwsNoSuchMethodError = const Throws(isNoSuchMethodError);
571
572 class _NoSuchMethodError extends TypeMatcher {
573 const _NoSuchMethodError(): super("NoSuchMethodError");
574 bool matches(item, Map matchState) => item is NoSuchMethodError;
575 }
576
577 /** A matcher for UnimplementedErrors. */
578 const isUnimplementedError = const _UnimplementedError();
579
580 /** A matcher for functions that throw Exception. */
581 const Matcher throwsUnimplementedError = const Throws(isUnimplementedError);
582
583 class _UnimplementedError extends TypeMatcher {
584 const _UnimplementedError(): super("UnimplementedError");
585 bool matches(item, Map matchState) => item is UnimplementedError;
586 }
587
588 /** A matcher for UnsupportedError. */
589 const isUnsupportedError = const _UnsupportedError();
590
591 /** A matcher for functions that throw UnsupportedError. */
592 const Matcher throwsUnsupportedError = const Throws(isUnsupportedError);
593
594 class _UnsupportedError extends TypeMatcher {
595 const _UnsupportedError(): super("UnsupportedError");
596 bool matches(item, Map matchState) => item is UnsupportedError;
597 }
598
599 /** A matcher for StateErrors. */
600 const isStateError = const _StateError();
601
602 /** A matcher for functions that throw StateError. */
603 const Matcher throwsStateError = const Throws(isStateError);
604
605 class _StateError extends TypeMatcher {
606 const _StateError(): super("StateError");
607 bool matches(item, Map matchState) => item is StateError;
608 }
609
610 /** A matcher for FallThroughError. */
611 const isFallThroughError = const _FallThroughError();
612
613 /** A matcher for functions that throw FallThroughError. */
614 const Matcher throwsFallThroughError = const Throws(isFallThroughError);
615
616 class _FallThroughError extends TypeMatcher {
617 const _FallThroughError(): super("FallThroughError");
618 bool matches(item, Map matchState) => item is FallThroughError;
619 }
620
621 /** A matcher for NullThrownError. */
622 const isNullThrownError = const _NullThrownError();
623
624 /** A matcher for functions that throw NullThrownError. */
625 const Matcher throwsNullThrownError = const Throws(isNullThrownError);
626
627 class _NullThrownError extends TypeMatcher {
628 const _NullThrownError(): super("NullThrownError");
629 bool matches(item, Map matchState) => item is NullThrownError;
630 }
631
632 /** A matcher for ConcurrentModificationError. */
633 const isConcurrentModificationError = const _ConcurrentModificationError();
634
635 /** A matcher for functions that throw ConcurrentModificationError. */
636 const Matcher throwsConcurrentModificationError =
637 const Throws(isConcurrentModificationError);
638
639 class _ConcurrentModificationError extends TypeMatcher {
640 const _ConcurrentModificationError(): super("ConcurrentModificationError");
641 bool matches(item, Map matchState) => item is ConcurrentModificationError;
642 }
643
644 /** A matcher for AbstractClassInstantiationError. */
645 const isAbstractClassInstantiationError =
646 const _AbstractClassInstantiationError();
647
648 /** A matcher for functions that throw AbstractClassInstantiationError. */
649 const Matcher throwsAbstractClassInstantiationError =
650 const Throws(isAbstractClassInstantiationError);
651
652 class _AbstractClassInstantiationError extends TypeMatcher {
653 const _AbstractClassInstantiationError() :
654 super("AbstractClassInstantiationError");
655 bool matches(item, Map matchState) => item is AbstractClassInstantiationError;
656 }
657
658 /** A matcher for CyclicInitializationError. */
659 const isCyclicInitializationError = const _CyclicInitializationError();
660
661 /** A matcher for functions that throw CyclicInitializationError. */
662 const Matcher throwsCyclicInitializationError =
663 const Throws(isCyclicInitializationError);
664
665 class _CyclicInitializationError extends TypeMatcher {
666 const _CyclicInitializationError(): super("CyclicInitializationError");
667 bool matches(item, Map matchState) => item is CyclicInitializationError;
668 }
669
670 /** A matcher for Map types. */
671 const isMap = const _IsMap();
518 672
519 class _IsMap extends TypeMatcher { 673 class _IsMap extends TypeMatcher {
520 const _IsMap() : super("Map"); 674 const _IsMap(): super("Map");
521 bool matches(item, Map matchState) => item is Map; 675 bool matches(item, Map matchState) => item is Map;
522 } 676 }
523 677
524 /// A matcher for List types. 678 /** A matcher for List types. */
525 const Matcher isList = const _IsList(); 679 const isList = const _IsList();
526 680
527 class _IsList extends TypeMatcher { 681 class _IsList extends TypeMatcher {
528 const _IsList() : super("List"); 682 const _IsList(): super("List");
529 bool matches(item, Map matchState) => item is List; 683 bool matches(item, Map matchState) => item is List;
530 } 684 }
531 685
532 /// Returns a matcher that matches if an object has a length property 686 /**
533 /// that matches [matcher]. 687 * Returns a matcher that matches if an object has a length property
688 * that matches [matcher].
689 */
534 Matcher hasLength(matcher) => new _HasLength(wrapMatcher(matcher)); 690 Matcher hasLength(matcher) => new _HasLength(wrapMatcher(matcher));
535 691
536 class _HasLength extends Matcher { 692 class _HasLength extends Matcher {
537 final Matcher _matcher; 693 final Matcher _matcher;
538 const _HasLength([Matcher matcher = null]) : this._matcher = matcher; 694 const _HasLength([Matcher matcher = null]): this._matcher = matcher;
539 695
540 bool matches(item, Map matchState) { 696 bool matches(item, Map matchState) {
541 try { 697 try {
542 // This is harmless code that will throw if no length property 698 // This is harmless code that will throw if no length property
543 // but subtle enough that an optimizer shouldn't strip it out. 699 // but subtle enough that an optimizer shouldn't strip it out.
544 if (item.length * item.length >= 0) { 700 if (item.length * item.length >= 0) {
545 return _matcher.matches(item.length, matchState); 701 return _matcher.matches(item.length, matchState);
546 } 702 }
547 } catch (e) {} 703 } catch (e) {}
548 return false; 704 return false;
(...skipping 10 matching lines...) Expand all
559 // property; we use the same trick as in matches(). 715 // property; we use the same trick as in matches().
560 if (item.length * item.length >= 0) { 716 if (item.length * item.length >= 0) {
561 return mismatchDescription.add('has length of '). 717 return mismatchDescription.add('has length of ').
562 addDescriptionOf(item.length); 718 addDescriptionOf(item.length);
563 } 719 }
564 } catch (e) {} 720 } catch (e) {}
565 return mismatchDescription.add('has no length property'); 721 return mismatchDescription.add('has no length property');
566 } 722 }
567 } 723 }
568 724
569 /// Returns a matcher that matches if the match argument contains 725 /**
570 /// the expected value. For [String]s this means substring matching; 726 * Returns a matcher that matches if the match argument contains
571 /// for [Map]s it means the map has the key, and for [Iterable]s 727 * the expected value. For [String]s this means substring matching;
572 /// (including [Iterable]s) it means the iterable has a matching 728 * for [Map]s it means the map has the key, and for [Iterable]s
573 /// element. In the case of iterables, [expected] can itself be a 729 * (including [Iterable]s) it means the iterable has a matching
574 /// matcher. 730 * element. In the case of iterables, [expected] can itself be a
731 * matcher.
732 */
575 Matcher contains(expected) => new _Contains(expected); 733 Matcher contains(expected) => new _Contains(expected);
576 734
577 class _Contains extends Matcher { 735 class _Contains extends Matcher {
736
578 final _expected; 737 final _expected;
579 738
580 const _Contains(this._expected); 739 const _Contains(this._expected);
581 740
582 bool matches(item, Map matchState) { 741 bool matches(item, Map matchState) {
583 if (item is String) { 742 if (item is String) {
584 return item.indexOf(_expected) >= 0; 743 return item.indexOf(_expected) >= 0;
585 } else if (item is Iterable) { 744 } else if (item is Iterable) {
586 if (_expected is Matcher) { 745 if (_expected is Matcher) {
587 return item.any((e) => _expected.matches(e, matchState)); 746 return item.any((e) => _expected.matches(e, matchState));
(...skipping 13 matching lines...) Expand all
601 Map matchState, bool verbose) { 760 Map matchState, bool verbose) {
602 if (item is String || item is Iterable || item is Map) { 761 if (item is String || item is Iterable || item is Map) {
603 return super.describeMismatch(item, mismatchDescription, matchState, 762 return super.describeMismatch(item, mismatchDescription, matchState,
604 verbose); 763 verbose);
605 } else { 764 } else {
606 return mismatchDescription.add('is not a string, map or iterable'); 765 return mismatchDescription.add('is not a string, map or iterable');
607 } 766 }
608 } 767 }
609 } 768 }
610 769
611 /// Returns a matcher that matches if the match argument is in 770 /**
612 /// the expected value. This is the converse of [contains]. 771 * Returns a matcher that matches if the match argument is in
772 * the expected value. This is the converse of [contains].
773 */
613 Matcher isIn(expected) => new _In(expected); 774 Matcher isIn(expected) => new _In(expected);
614 775
615 class _In extends Matcher { 776 class _In extends Matcher {
777
616 final _expected; 778 final _expected;
617 779
618 const _In(this._expected); 780 const _In(this._expected);
619 781
620 bool matches(item, Map matchState) { 782 bool matches(item, Map matchState) {
621 if (_expected is String) { 783 if (_expected is String) {
622 return _expected.indexOf(item) >= 0; 784 return _expected.indexOf(item) >= 0;
623 } else if (_expected is Iterable) { 785 } else if (_expected is Iterable) {
624 return _expected.any((e) => e == item); 786 return _expected.any((e) => e == item);
625 } else if (_expected is Map) { 787 } else if (_expected is Map) {
626 return _expected.containsKey(item); 788 return _expected.containsKey(item);
627 } 789 }
628 return false; 790 return false;
629 } 791 }
630 792
631 Description describe(Description description) => 793 Description describe(Description description) =>
632 description.add('is in ').addDescriptionOf(_expected); 794 description.add('is in ').addDescriptionOf(_expected);
633 } 795 }
634 796
635 /// Returns a matcher that uses an arbitrary function that returns 797 /**
636 /// true or false for the actual value. For example: 798 * Returns a matcher that uses an arbitrary function that returns
637 /// 799 * true or false for the actual value. For example:
638 /// expect(v, predicate((x) => ((x % 2) == 0), "is even")) 800 *
639 Matcher predicate(bool f(value), [String description = 'satisfies function']) => 801 * expect(v, predicate((x) => ((x % 2) == 0), "is even"))
802 */
803 Matcher predicate(Function f, [description = 'satisfies function']) =>
640 new _Predicate(f, description); 804 new _Predicate(f, description);
641 805
642 typedef bool _PredicateFunction(value); 806 class _Predicate extends Matcher {
643 807
644 class _Predicate extends Matcher { 808 final Function _matcher;
645 final _PredicateFunction _matcher;
646 final String _description; 809 final String _description;
647 810
648 const _Predicate(this._matcher, this._description); 811 const _Predicate(this._matcher, this._description);
649 812
650 bool matches(item, Map matchState) => _matcher(item); 813 bool matches(item, Map matchState) => _matcher(item);
651 814
652 Description describe(Description description) => 815 Description describe(Description description) =>
653 description.add(_description); 816 description.add(_description);
654 } 817 }
655 818
656 /// A useful utility class for implementing other matchers through inheritance. 819 /**
657 /// Derived classes should call the base constructor with a feature name and 820 * A useful utility class for implementing other matchers through inheritance.
658 /// description, and an instance matcher, and should implement the 821 * Derived classes should call the base constructor with a feature name and
659 /// [featureValueOf] abstract method. 822 * description, and an instance matcher, and should implement the
660 /// 823 * [featureValueOf] abstract method.
661 /// The feature description will typically describe the item and the feature, 824 *
662 /// while the feature name will just name the feature. For example, we may 825 * The feature description will typically describe the item and the feature,
663 /// have a Widget class where each Widget has a price; we could make a 826 * while the feature name will just name the feature. For example, we may
664 /// [CustomMatcher] that can make assertions about prices with: 827 * have a Widget class where each Widget has a price; we could make a
665 /// 828 * [CustomMatcher] that can make assertions about prices with:
666 /// class HasPrice extends CustomMatcher { 829 *
667 /// const HasPrice(matcher) : 830 * class HasPrice extends CustomMatcher {
668 /// super("Widget with price that is", "price", matcher); 831 * const HasPrice(matcher) :
669 /// featureValueOf(actual) => actual.price; 832 * super("Widget with price that is", "price", matcher);
670 /// } 833 * featureValueOf(actual) => actual.price;
671 /// 834 * }
672 /// and then use this for example like: 835 *
673 /// 836 * and then use this for example like:
674 /// expect(inventoryItem, new HasPrice(greaterThan(0))); 837 *
838 * expect(inventoryItem, new HasPrice(greaterThan(0)));
839 */
675 class CustomMatcher extends Matcher { 840 class CustomMatcher extends Matcher {
676 final String _featureDescription; 841 final String _featureDescription;
677 final String _featureName; 842 final String _featureName;
678 final Matcher _matcher; 843 final Matcher _matcher;
679 844
680 CustomMatcher(this._featureDescription, this._featureName, matcher) 845 CustomMatcher(this._featureDescription, this._featureName, matcher)
681 : this._matcher = wrapMatcher(matcher); 846 : this._matcher = wrapMatcher(matcher);
682 847
683 /// Override this to extract the interesting feature. 848 /** Override this to extract the interesting feature.*/
684 featureValueOf(actual) => actual; 849 featureValueOf(actual) => actual;
685 850
686 bool matches(item, Map matchState) { 851 bool matches(item, Map matchState) {
687 var f = featureValueOf(item); 852 var f = featureValueOf(item);
688 if (_matcher.matches(f, matchState)) return true; 853 if (_matcher.matches(f, matchState)) return true;
689 addStateInfo(matchState, {'feature': f}); 854 addStateInfo(matchState, {'feature': f});
690 return false; 855 return false;
691 } 856 }
692 857
693 Description describe(Description description) => 858 Description describe(Description description) =>
694 description.add(_featureDescription).add(' ').addDescriptionOf(_matcher); 859 description.add(_featureDescription).add(' ').addDescriptionOf(_matcher);
695 860
696 Description describeMismatch(item, Description mismatchDescription, 861 Description describeMismatch(item, Description mismatchDescription,
697 Map matchState, bool verbose) { 862 Map matchState, bool verbose) {
698 mismatchDescription.add('has ').add(_featureName).add(' with value '). 863 mismatchDescription.add('has ').add(_featureName).add(' with value ').
699 addDescriptionOf(matchState['feature']); 864 addDescriptionOf(matchState['feature']);
700 var innerDescription = new StringDescription(); 865 var innerDescription = new StringDescription();
701 _matcher.describeMismatch(matchState['feature'], innerDescription, 866 _matcher.describeMismatch(matchState['feature'], innerDescription,
702 matchState['state'], verbose); 867 matchState['state'], verbose);
703 if (innerDescription.length > 0) { 868 if (innerDescription.length > 0) {
704 mismatchDescription.add(' which ').add(innerDescription.toString()); 869 mismatchDescription.add(' which ').add(innerDescription.toString());
705 } 870 }
706 return mismatchDescription; 871 return mismatchDescription;
707 } 872 }
708 } 873 }
OLDNEW
« no previous file with comments | « pkg/matcher/lib/mirror_matchers.dart ('k') | pkg/matcher/lib/src/description.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698