Line data Source code
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 : import 'feature_matcher.dart';
6 : import 'interfaces.dart';
7 : import 'type_matcher.dart';
8 : import 'util.dart';
9 :
10 : /// Returns a matcher that matches the isEmpty property.
11 : const Matcher isEmpty = _Empty();
12 :
13 : class _Empty extends Matcher {
14 11 : const _Empty();
15 :
16 0 : @override
17 0 : bool matches(Object? item, Map matchState) => (item as dynamic).isEmpty;
18 :
19 0 : @override
20 0 : Description describe(Description description) => description.add('empty');
21 : }
22 :
23 : /// Returns a matcher that matches the isNotEmpty property.
24 : const Matcher isNotEmpty = _NotEmpty();
25 :
26 : class _NotEmpty extends Matcher {
27 11 : const _NotEmpty();
28 :
29 0 : @override
30 0 : bool matches(Object? item, Map matchState) => (item as dynamic).isNotEmpty;
31 :
32 0 : @override
33 0 : Description describe(Description description) => description.add('non-empty');
34 : }
35 :
36 : /// A matcher that matches any null value.
37 : const Matcher isNull = _IsNull();
38 :
39 : /// A matcher that matches any non-null value.
40 : const Matcher isNotNull = _IsNotNull();
41 :
42 : class _IsNull extends Matcher {
43 11 : const _IsNull();
44 2 : @override
45 : bool matches(Object? item, Map matchState) => item == null;
46 0 : @override
47 0 : Description describe(Description description) => description.add('null');
48 : }
49 :
50 : class _IsNotNull extends Matcher {
51 11 : const _IsNotNull();
52 2 : @override
53 : bool matches(Object? item, Map matchState) => item != null;
54 0 : @override
55 0 : Description describe(Description description) => description.add('not null');
56 : }
57 :
58 : /// A matcher that matches the Boolean value true.
59 : const Matcher isTrue = _IsTrue();
60 :
61 : /// A matcher that matches anything except the Boolean value true.
62 : const Matcher isFalse = _IsFalse();
63 :
64 : class _IsTrue extends Matcher {
65 11 : const _IsTrue();
66 3 : @override
67 3 : bool matches(Object? item, Map matchState) => item == true;
68 0 : @override
69 0 : Description describe(Description description) => description.add('true');
70 : }
71 :
72 : class _IsFalse extends Matcher {
73 11 : const _IsFalse();
74 1 : @override
75 1 : bool matches(Object? item, Map matchState) => item == false;
76 0 : @override
77 0 : Description describe(Description description) => description.add('false');
78 : }
79 :
80 : /// A matcher that matches the numeric value NaN.
81 : const Matcher isNaN = _IsNaN();
82 :
83 : /// A matcher that matches any non-NaN value.
84 : const Matcher isNotNaN = _IsNotNaN();
85 :
86 : class _IsNaN extends FeatureMatcher<num> {
87 11 : const _IsNaN();
88 0 : @override
89 : bool typedMatches(num item, Map matchState) =>
90 0 : double.nan.compareTo(item) == 0;
91 0 : @override
92 0 : Description describe(Description description) => description.add('NaN');
93 : }
94 :
95 : class _IsNotNaN extends FeatureMatcher<num> {
96 11 : const _IsNotNaN();
97 0 : @override
98 : bool typedMatches(num item, Map matchState) =>
99 0 : double.nan.compareTo(item) != 0;
100 0 : @override
101 0 : Description describe(Description description) => description.add('not NaN');
102 : }
103 :
104 : /// Returns a matches that matches if the value is the same instance
105 : /// as [expected], using [identical].
106 0 : Matcher same(Object? expected) => _IsSameAs(expected);
107 :
108 : class _IsSameAs extends Matcher {
109 : final Object? _expected;
110 0 : const _IsSameAs(this._expected);
111 0 : @override
112 0 : bool matches(Object? item, Map matchState) => identical(item, _expected);
113 : // If all types were hashable we could show a hash here.
114 0 : @override
115 : Description describe(Description description) =>
116 0 : description.add('same instance as ').addDescriptionOf(_expected);
117 : }
118 :
119 : /// A matcher that matches any value.
120 : const Matcher anything = _IsAnything();
121 :
122 : class _IsAnything extends Matcher {
123 11 : const _IsAnything();
124 0 : @override
125 : bool matches(Object? item, Map matchState) => true;
126 0 : @override
127 0 : Description describe(Description description) => description.add('anything');
128 : }
129 :
130 : /// **DEPRECATED** Use [isA] instead.
131 : ///
132 : /// A matcher that matches if an object is an instance of [T] (or a subtype).
133 : @Deprecated('Use `isA<MyType>()` instead.')
134 : // ignore: camel_case_types
135 : class isInstanceOf<T> extends TypeMatcher<T> {
136 0 : const isInstanceOf();
137 : }
138 :
139 : /// A matcher that matches a function call against no exception.
140 : ///
141 : /// The function will be called once. Any exceptions will be silently swallowed.
142 : /// The value passed to expect() should be a reference to the function.
143 : /// Note that the function cannot take arguments; to handle this
144 : /// a wrapper will have to be created.
145 : const Matcher returnsNormally = _ReturnsNormally();
146 :
147 : class _ReturnsNormally extends FeatureMatcher<Function> {
148 11 : const _ReturnsNormally();
149 :
150 0 : @override
151 : bool typedMatches(Function f, Map matchState) {
152 : try {
153 0 : f();
154 : return true;
155 : } catch (e, s) {
156 0 : addStateInfo(matchState, {'exception': e, 'stack': s});
157 : return false;
158 : }
159 : }
160 :
161 0 : @override
162 : Description describe(Description description) =>
163 0 : description.add('return normally');
164 :
165 0 : @override
166 : Description describeTypedMismatch(Function item,
167 : Description mismatchDescription, Map matchState, bool verbose) {
168 0 : mismatchDescription.add('threw ').addDescriptionOf(matchState['exception']);
169 : if (verbose) {
170 0 : mismatchDescription.add(' at ').add(matchState['stack'].toString());
171 : }
172 : return mismatchDescription;
173 : }
174 : }
175 :
176 : /// A matcher for [Map].
177 : const isMap = TypeMatcher<Map>();
178 :
179 : /// A matcher for [List].
180 : const isList = TypeMatcher<List>();
181 :
182 : /// Returns a matcher that matches if an object has a length property
183 : /// that matches [matcher].
184 0 : Matcher hasLength(Object? matcher) => _HasLength(wrapMatcher(matcher));
185 :
186 : class _HasLength extends Matcher {
187 : final Matcher _matcher;
188 0 : const _HasLength(this._matcher);
189 :
190 0 : @override
191 : bool matches(Object? item, Map matchState) {
192 : try {
193 0 : final length = (item as dynamic).length;
194 0 : return _matcher.matches(length, matchState);
195 : } catch (e) {
196 : return false;
197 : }
198 : }
199 :
200 0 : @override
201 : Description describe(Description description) =>
202 0 : description.add('an object with length of ').addDescriptionOf(_matcher);
203 :
204 0 : @override
205 : Description describeMismatch(Object? item, Description mismatchDescription,
206 : Map matchState, bool verbose) {
207 : try {
208 0 : final length = (item as dynamic).length;
209 0 : return mismatchDescription.add('has length of ').addDescriptionOf(length);
210 : } catch (e) {
211 0 : return mismatchDescription.add('has no length property');
212 : }
213 : }
214 : }
215 :
216 : /// Returns a matcher that matches if the match argument contains the expected
217 : /// value.
218 : ///
219 : /// For [String]s this means substring matching;
220 : /// for [Map]s it means the map has the key, and for [Iterable]s
221 : /// it means the iterable has a matching element. In the case of iterables,
222 : /// [expected] can itself be a matcher.
223 0 : Matcher contains(Object? expected) => _Contains(expected);
224 :
225 : class _Contains extends Matcher {
226 : final Object? _expected;
227 :
228 0 : const _Contains(this._expected);
229 :
230 0 : @override
231 : bool matches(Object? item, Map matchState) {
232 0 : var expected = _expected;
233 0 : if (item is String) {
234 0 : return expected is Pattern && item.contains(expected);
235 0 : } else if (item is Iterable) {
236 0 : if (expected is Matcher) {
237 0 : return item.any((e) => expected.matches(e, matchState));
238 : } else {
239 0 : return item.contains(_expected);
240 : }
241 0 : } else if (item is Map) {
242 0 : return item.containsKey(_expected);
243 : }
244 : return false;
245 : }
246 :
247 0 : @override
248 : Description describe(Description description) =>
249 0 : description.add('contains ').addDescriptionOf(_expected);
250 :
251 0 : @override
252 : Description describeMismatch(Object? item, Description mismatchDescription,
253 : Map matchState, bool verbose) {
254 0 : if (item is String || item is Iterable || item is Map) {
255 : return super
256 0 : .describeMismatch(item, mismatchDescription, matchState, verbose);
257 : } else {
258 0 : return mismatchDescription.add('is not a string, map or iterable');
259 : }
260 : }
261 : }
262 :
263 : /// Returns a matcher that matches if the match argument is in
264 : /// the expected value. This is the converse of [contains].
265 0 : Matcher isIn(Object? expected) {
266 0 : if (expected is Iterable) {
267 0 : return _In(expected, expected.contains);
268 0 : } else if (expected is String) {
269 0 : return _In<Pattern>(expected, expected.contains);
270 0 : } else if (expected is Map) {
271 0 : return _In(expected, expected.containsKey);
272 : }
273 :
274 0 : throw ArgumentError.value(
275 : expected, 'expected', 'Only Iterable, Map, and String are supported.');
276 : }
277 :
278 : class _In<T> extends FeatureMatcher<T> {
279 : final Object _source;
280 : final bool Function(T) _containsFunction;
281 :
282 0 : const _In(this._source, this._containsFunction);
283 :
284 0 : @override
285 0 : bool typedMatches(T item, Map matchState) => _containsFunction(item);
286 :
287 0 : @override
288 : Description describe(Description description) =>
289 0 : description.add('is in ').addDescriptionOf(_source);
290 : }
291 :
292 : /// Returns a matcher that uses an arbitrary function that returns
293 : /// true or false for the actual value.
294 : ///
295 : /// For example:
296 : ///
297 : /// expect(v, predicate((x) => ((x % 2) == 0), "is even"))
298 0 : Matcher predicate<T>(bool Function(T) f,
299 : [String description = 'satisfies function']) =>
300 0 : _Predicate(f, description);
301 :
302 : typedef _PredicateFunction<T> = bool Function(T value);
303 :
304 : class _Predicate<T> extends FeatureMatcher<T> {
305 : final _PredicateFunction<T> _matcher;
306 : final String _description;
307 :
308 0 : _Predicate(this._matcher, this._description);
309 :
310 0 : @override
311 0 : bool typedMatches(T item, Map matchState) => _matcher(item);
312 :
313 0 : @override
314 : Description describe(Description description) =>
315 0 : description.add(_description);
316 : }
|