| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'feature_matcher.dart'; | |
| import 'interfaces.dart'; | |
| import 'type_matcher.dart'; | |
| import 'util.dart'; | |
| /// Returns a matcher that matches the isEmpty property. | |
| const Matcher isEmpty = _Empty(); | |
| class _Empty extends Matcher { | |
| const _Empty(); | |
| @override | |
| bool matches(Object? item, Map matchState) => | |
| (item as dynamic).isEmpty as bool; | |
| @override | |
| Description describe(Description description) => description.add('empty'); | |
| } | |
| /// Returns a matcher that matches the isNotEmpty property. | |
| const Matcher isNotEmpty = _NotEmpty(); | |
| class _NotEmpty extends Matcher { | |
| const _NotEmpty(); | |
| @override | |
| bool matches(Object? item, Map matchState) => | |
| (item as dynamic).isNotEmpty as bool; | |
| @override | |
| Description describe(Description description) => description.add('non-empty'); | |
| } | |
| /// A matcher that matches any null value. | |
| const Matcher isNull = _IsNull(); | |
| /// A matcher that matches any non-null value. | |
| const Matcher isNotNull = _IsNotNull(); | |
| class _IsNull extends Matcher { | |
| const _IsNull(); | |
| @override | |
| bool matches(Object? item, Map matchState) => item == null; | |
| @override | |
| Description describe(Description description) => description.add('null'); | |
| } | |
| class _IsNotNull extends Matcher { | |
| const _IsNotNull(); | |
| @override | |
| bool matches(Object? item, Map matchState) => item != null; | |
| @override | |
| Description describe(Description description) => description.add('not null'); | |
| } | |
| /// A matcher that matches the Boolean value true. | |
| const Matcher isTrue = _IsTrue(); | |
| /// A matcher that matches anything except the Boolean value true. | |
| const Matcher isFalse = _IsFalse(); | |
| class _IsTrue extends Matcher { | |
| const _IsTrue(); | |
| @override | |
| bool matches(Object? item, Map matchState) => item == true; | |
| @override | |
| Description describe(Description description) => description.add('true'); | |
| } | |
| class _IsFalse extends Matcher { | |
| const _IsFalse(); | |
| @override | |
| bool matches(Object? item, Map matchState) => item == false; | |
| @override | |
| Description describe(Description description) => description.add('false'); | |
| } | |
| /// A matcher that matches the numeric value NaN. | |
| const Matcher isNaN = _IsNaN(); | |
| /// A matcher that matches any non-NaN value. | |
| const Matcher isNotNaN = _IsNotNaN(); | |
| class _IsNaN extends FeatureMatcher<num> { | |
| const _IsNaN(); | |
| @override | |
| bool typedMatches(num item, Map matchState) => | |
| double.nan.compareTo(item) == 0; | |
| @override | |
| Description describe(Description description) => description.add('NaN'); | |
| } | |
| class _IsNotNaN extends FeatureMatcher<num> { | |
| const _IsNotNaN(); | |
| @override | |
| bool typedMatches(num item, Map matchState) => | |
| double.nan.compareTo(item) != 0; | |
| @override | |
| Description describe(Description description) => description.add('not NaN'); | |
| } | |
| /// Returns a matches that matches if the value is the same instance | |
| /// as [expected], using [identical]. | |
| Matcher same(Object? expected) => _IsSameAs(expected); | |
| class _IsSameAs extends Matcher { | |
| final Object? _expected; | |
| const _IsSameAs(this._expected); | |
| @override | |
| bool matches(Object? item, Map matchState) => identical(item, _expected); | |
| // If all types were hashable we could show a hash here. | |
| @override | |
| Description describe(Description description) => | |
| description.add('same instance as ').addDescriptionOf(_expected); | |
| } | |
| /// A matcher that matches any value. | |
| const Matcher anything = _IsAnything(); | |
| class _IsAnything extends Matcher { | |
| const _IsAnything(); | |
| @override | |
| bool matches(Object? item, Map matchState) => true; | |
| @override | |
| Description describe(Description description) => description.add('anything'); | |
| } | |
| /// **DEPRECATED** Use [isA] instead. | |
| /// | |
| /// A matcher that matches if an object is an instance of [T] (or a subtype). | |
| @Deprecated('Use `isA<MyType>()` instead.') | |
| // ignore: camel_case_types | |
| class isInstanceOf<T> extends TypeMatcher<T> { | |
| const isInstanceOf(); | |
| } | |
| /// A matcher that matches a function call against no exception. | |
| /// | |
| /// The function will be called once. Any exceptions will be silently swallowed. | |
| /// The value passed to expect() should be a reference to the function. | |
| /// Note that the function cannot take arguments; to handle this | |
| /// a wrapper will have to be created. | |
| const Matcher returnsNormally = _ReturnsNormally(); | |
| class _ReturnsNormally extends FeatureMatcher<Function> { | |
| const _ReturnsNormally(); | |
| @override | |
| bool typedMatches(Function f, Map matchState) { | |
| try { | |
| // ignore: unnecessary_cast | |
| (f as Function)(); | |
| return true; | |
| } catch (e, s) { | |
| addStateInfo(matchState, {'exception': e, 'stack': s}); | |
| return false; | |
| } | |
| } | |
| @override | |
| Description describe(Description description) => | |
| description.add('return normally'); | |
| @override | |
| Description describeTypedMismatch( | |
| Function item, | |
| Description mismatchDescription, | |
| Map matchState, | |
| bool verbose, | |
| ) { | |
| mismatchDescription.add('threw ').addDescriptionOf(matchState['exception']); | |
| if (verbose) { | |
| mismatchDescription.add(' at ').add(matchState['stack'].toString()); | |
| } | |
| return mismatchDescription; | |
| } | |
| } | |
| /// A matcher for [Map]. | |
| const isMap = TypeMatcher<Map>(); | |
| /// A matcher for [List]. | |
| const isList = TypeMatcher<List>(); | |
| /// Returns a matcher that matches if an object has a length property | |
| /// that matches [matcher]. | |
| Matcher hasLength(Object? matcher) => _HasLength(wrapMatcher(matcher)); | |
| class _HasLength extends Matcher { | |
| final Matcher _matcher; | |
| const _HasLength(this._matcher); | |
| @override | |
| bool matches(Object? item, Map matchState) { | |
| try { | |
| final length = (item as dynamic).length; | |
| return _matcher.matches(length, matchState); | |
| } catch (e) { | |
| return false; | |
| } | |
| } | |
| @override | |
| Description describe(Description description) => | |
| description.add('an object with length of ').addDescriptionOf(_matcher); | |
| @override | |
| Description describeMismatch( | |
| Object? item, | |
| Description mismatchDescription, | |
| Map matchState, | |
| bool verbose, | |
| ) { | |
| try { | |
| final length = (item as dynamic).length; | |
| return mismatchDescription.add('has length of ').addDescriptionOf(length); | |
| } catch (e) { | |
| return mismatchDescription.add('has no length property'); | |
| } | |
| } | |
| } | |
| /// Returns a matcher that matches if the match argument contains the expected | |
| /// value. | |
| /// | |
| /// For [String]s this means substring matching; | |
| /// for [Map]s it means the map has the key, and for [Iterable]s | |
| /// it means the iterable has a matching element. In the case of iterables, | |
| /// [expected] can itself be a matcher. | |
| Matcher contains(Object? expected) => _Contains(expected); | |
| class _Contains extends Matcher { | |
| final Object? _expected; | |
| const _Contains(this._expected); | |
| @override | |
| bool matches(Object? item, Map matchState) { | |
| var expected = _expected; | |
| if (item is String) { | |
| return expected is Pattern && item.contains(expected); | |
| } else if (item is Iterable) { | |
| if (expected is Matcher) { | |
| return item.any((e) => expected.matches(e, matchState)); | |
| } else { | |
| return item.contains(_expected); | |
| } | |
| } else if (item is Map) { | |
| return item.containsKey(_expected); | |
| } | |
| return false; | |
| } | |
| @override | |
| Description describe(Description description) => | |
| description.add('contains ').addDescriptionOf(_expected); | |
| @override | |
| Description describeMismatch( | |
| Object? item, | |
| Description mismatchDescription, | |
| Map matchState, | |
| bool verbose, | |
| ) { | |
| if (item is String || item is Iterable || item is Map) { | |
| super.describeMismatch(item, mismatchDescription, matchState, verbose); | |
| mismatchDescription.add('does not contain ').addDescriptionOf(_expected); | |
| return mismatchDescription; | |
| } else { | |
| return mismatchDescription.add('is not a string, map or iterable'); | |
| } | |
| } | |
| } | |
| /// Returns a matcher that matches if the match argument is in | |
| /// the expected value. This is the converse of [contains]. | |
| Matcher isIn(Object? expected) { | |
| if (expected is Iterable) { | |
| return _In(expected, expected.contains); | |
| } else if (expected is String) { | |
| return _In<Pattern>(expected, expected.contains); | |
| } else if (expected is Map) { | |
| return _In(expected, expected.containsKey); | |
| } | |
| throw ArgumentError.value( | |
| expected, | |
| 'expected', | |
| 'Only Iterable, Map, and String are supported.', | |
| ); | |
| } | |
| class _In<T> extends FeatureMatcher<T> { | |
| final Object _source; | |
| final bool Function(T) _containsFunction; | |
| const _In(this._source, this._containsFunction); | |
| @override | |
| bool typedMatches(T item, Map matchState) => _containsFunction(item); | |
| @override | |
| Description describe(Description description) => | |
| description.add('is in ').addDescriptionOf(_source); | |
| } | |
| /// Returns a matcher that uses an arbitrary function that returns whether the | |
| /// value is considered a match. | |
| /// | |
| /// For example: | |
| /// | |
| /// expect(actual, predicate<num>((v) => (v % 2) == 0, 'is even')); | |
| /// | |
| /// Use this method when a value is checked for one conceptual property | |
| /// described by [description]. | |
| /// | |
| /// If the value can be rejected for more than one reason prefer using [isA] and | |
| /// the [TypeMatcher.having] API to build up a matcher with output that can | |
| /// distinquish between them. | |
| /// | |
| /// Using an explicit generict argument allows a passed function literal to have | |
| /// an inferred argument type of [T], and values of the wrong type will be | |
| /// rejected with an informative message. | |
| Matcher predicate<T>( | |
| bool Function(T) f, [ | |
| String description = 'satisfies function', | |
| ]) => _Predicate(f, description); | |
| class _Predicate<T> extends FeatureMatcher<T> { | |
| final bool Function(T) _matcher; | |
| final String _description; | |
| _Predicate(this._matcher, this._description); | |
| @override | |
| bool typedMatches(T item, Map matchState) => _matcher(item); | |
| @override | |
| Description describe(Description description) => | |
| description.add(_description); | |
| } |