OLD | NEW |
---|---|
(Empty) | |
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 /** Returns a matcher that matches any null value. */ | |
6 IMatcher isNull() => new _IsNull(); | |
Bob Nystrom
2012/05/30 23:23:51
Make it a var:
final isNull = const _IsNull();
P
gram
2012/06/01 17:33:15
Done.
| |
7 | |
8 /** Returns a matcher that matches any non-null value. */ | |
9 IMatcher isNotNull() => isNot(isNull()); | |
10 | |
11 class _IsNull extends Matcher { | |
12 bool matches(item) => (item == null); | |
Bob Nystrom
2012/05/30 23:23:51
Remove parens:
bool matches(item) => item == nul
gram
2012/06/01 17:33:15
Done.
| |
13 IDescription describe(IDescription description) => description.append('null'); | |
14 } | |
15 | |
16 /** Returns a matcher that matches the Boolean value true. */ | |
17 IMatcher isTrue() => new _IsTrue(); | |
18 | |
19 /** Returns a matcher that matches anything except the Boolean value true. */ | |
20 IMatcher isFalse() => isNot(isTrue()); | |
21 | |
22 class _IsTrue extends Matcher { | |
23 | |
Bob Nystrom
2012/05/30 23:23:51
Delete this blank line.
gram
2012/06/01 17:33:15
Done.
| |
24 bool matches(item) => (item == true); | |
Bob Nystrom
2012/05/30 23:23:51
Remove unneeded parens.
gram
2012/06/01 17:33:15
Done.
| |
25 | |
26 IDescription describe(IDescription description) => description.append('true'); | |
27 } | |
28 | |
29 /** Returns a matches that matches if the value is the same instance | |
Bob Nystrom
2012/05/30 23:23:51
Can you format this with the text on the next line
gram
2012/06/01 17:33:15
Done.
| |
30 * as [object] (===). | |
Bob Nystrom
2012/05/30 23:23:51
Put === in backticks to code format it: as [object
gram
2012/06/01 17:33:15
Done.
| |
31 */ | |
32 IMatcher same(object) => new _IsSame(object); | |
Bob Nystrom
2012/05/30 23:23:51
"isSameAs"?
gram
2012/06/01 17:33:15
Done.
| |
33 | |
34 class _IsSame extends Matcher { | |
35 | |
36 var _object; | |
Bob Nystrom
2012/05/30 23:23:51
Other matcher classes use "_val" for this. Should
gram
2012/06/01 17:33:15
Done.
| |
37 | |
38 _IsSame(this._object); | |
39 | |
40 bool matches(item) => (item === _object); | |
Bob Nystrom
2012/05/30 23:23:51
Unnecessary ().
gram
2012/06/01 17:33:15
Done.
| |
41 | |
42 // If all types were hashable we could show a hash here | |
Bob Nystrom
2012/05/30 23:23:51
True. :(
Add a "." at the end of the sentence.
gram
2012/06/01 17:33:15
Done.
| |
43 IDescription describe(IDescription description) => | |
44 description.append('same instance as ').appendDescriptionOf(_object); | |
45 } | |
46 | |
47 /** Returns a matcher that matches if two objects are equal (==). */ | |
48 IMatcher equals(obj) => new _IsEqual(obj); | |
49 | |
50 class _IsEqual extends Matcher { | |
51 var _object; | |
52 | |
53 _IsEqual(this._object); | |
54 | |
55 bool matches(item) => (item == _object); | |
56 | |
57 IDescription describe(IDescription description) { | |
58 //if (_object is IMatcher) { | |
Bob Nystrom
2012/05/30 23:23:51
Remove commented out code.
gram
2012/06/01 17:33:15
Done.
| |
59 // return description.append('<'). | |
60 // appendDescriptionOf(_object). | |
61 // append('>'); | |
62 //} else { | |
63 return description.appendDescriptionOf(_object); | |
64 //} | |
65 } | |
66 } | |
67 | |
68 /** Returns a matcher that matches any value. */ | |
69 IMatcher anything([description = 'anything']) => new _IsAnything(description); | |
Bob Nystrom
2012/05/30 23:23:51
Can we get rid of description and make this a cons
gram
2012/06/01 17:33:15
Done.
| |
70 | |
71 class _IsAnything extends Matcher { | |
72 var _description; | |
73 | |
74 _IsAnything([this._description = 'anything']); | |
75 | |
76 bool matches(item) => true; | |
77 | |
78 IDescription describe(IDescription description) => | |
79 description.append(_description); | |
80 } | |
81 | |
82 /** | |
83 * Returns a matcher that matches functions that throw exceptions when called. | |
84 * The value passed to expect() should be a reference to the function. | |
85 * Note that the function cannot take arguments; to handle this | |
86 * a wrapper will have to be created. | |
87 * The function will be called once. | |
88 */ | |
89 IMatcher throwsException() => new _Throws(); | |
Bob Nystrom
2012/05/30 23:23:51
"throwsException" -> "throws"?
gram
2012/06/01 17:33:15
Done.
| |
90 | |
91 /** | |
92 * Returns a matcher that matches a function call against an exception, | |
93 * which is in turn constrained by a [matcher]. | |
94 * The value passed to expect() should be a reference to the function. | |
95 * Note that the function cannot take arguments; to handle this | |
96 * a wrapper will have to be created. | |
97 * The function will be called once upon success, or twice upon failure | |
98 * (the second time to get the failure description). | |
99 */ | |
100 IMatcher throwsExceptionWhich(IMatcher matcher) => new _Throws(matcher); | |
101 /** | |
Bob Nystrom
2012/05/30 23:23:51
Add blank line above comment.
gram
2012/06/01 17:33:15
Done.
| |
102 * Returns a matcher that matches a function call against no exception. | |
103 * The function will be called once. Any exceptions will be silently swallowed. | |
104 * The value passed to expect() should be a reference to the function. | |
105 * Note that the function cannot take arguments; to handle this | |
106 * a wrapper will have to be created. | |
107 */ | |
108 IMatcher returnsNormally() => new _ReturnsNormally(); | |
Bob Nystrom
2012/05/30 23:23:51
Maybe I don't the intent here, but it seems to me
gram
2012/06/01 17:33:15
Yes - this will catch an exception and return fals
Bob Nystrom
2012/06/01 18:22:22
What happens when exceptions are thrown outside of
| |
109 | |
110 class _Throws extends Matcher { | |
111 IMatcher _matcher; | |
112 | |
113 _Throws([IMatcher this._matcher = null]) { | |
114 if (_matcher != null && !(_matcher is Matcher)) { | |
Bob Nystrom
2012/05/30 23:23:51
You can delete this. Dart will validate this for y
gram
2012/06/01 17:33:15
Done.
gram
2012/06/01 17:33:15
Done.
| |
115 throw new IllegalArgumentException('Throws parameter must be matcher'); | |
116 } | |
117 } | |
118 | |
119 bool matches(item) { | |
120 try { | |
121 item(); | |
122 return false; | |
123 } catch (final e) { | |
124 return _matcher == null || _matcher.matches(e); | |
125 } | |
126 } | |
127 | |
128 IDescription describe(IDescription description) { | |
129 if (_matcher == null) { | |
130 return description.append("throws an exception"); | |
131 } else { | |
132 return description.append('throws an exception which needs to match '). | |
Bob Nystrom
2012/05/30 23:23:51
"which needs to match" -> "which matches"?
gram
2012/06/01 17:33:15
Done.
| |
133 appendDescriptionOf(_matcher); | |
134 } | |
135 } | |
136 | |
137 IDescription describeMismatch(item, IDescription mismatchDescription) { | |
138 try { | |
139 item(); | |
140 return mismatchDescription.append(' no exception'); | |
141 } catch (final e) { | |
142 return mismatchDescription.append(' exception does not match '). | |
143 appendDescriptionOf(_matcher); | |
144 } | |
145 } | |
146 } | |
147 | |
148 class _ReturnsNormally extends Matcher { | |
149 bool matches(f) { | |
150 try { | |
151 f(); | |
152 return true; | |
153 } catch (final e) { | |
154 return false; | |
155 } | |
156 } | |
157 IDescription describe(IDescription description) => | |
158 description.append("return normally"); | |
159 IDescription describeMismatch(item, IDescription mismatchDescription) { | |
160 return mismatchDescription.append(' threw exception'); | |
161 } | |
162 } | |
163 | |
164 /** | |
165 * Returns a matcher that matches if an object is an instance | |
166 * of a (super)[type]. | |
Bob Nystrom
2012/05/30 23:23:51
This is worded a bit confusingly. Maybe:
"if an o
gram
2012/06/01 17:33:15
Done.
| |
167 * | |
168 * As types are not first class objects in Dart we can only | |
169 * approximate this test, by using a generic wrapper named Type. | |
Bob Nystrom
2012/05/30 23:23:51
Delete ","
gram
2012/06/01 17:33:15
Done.
| |
170 * | |
171 * For example, to test whether 'bar' is an instance of type | |
172 * 'Foo', we would write: | |
173 * | |
174 * expect(bar, instanceOf(new Type<Foo>())); | |
175 * | |
176 * To get better error message, supply a name when creating the | |
177 * Type wrapper; e.g.: | |
178 * | |
179 * expect(bar, instanceOf(new Type<Foo>('Foo'))); | |
180 */ | |
181 IMatcher isInstanceOf(Type type) => new _IsInstanceOf(type); | |
182 | |
183 class Type<T> { | |
Bob Nystrom
2012/05/30 23:23:51
How about making this implement IMatcher directly
gram
2012/06/01 17:33:15
Done.
| |
184 String _name; | |
185 Type([this._name = 'specified type']); | |
186 bool isInstance(obj) => obj is T; | |
187 String toString() => _name; | |
188 } | |
189 | |
190 class _IsInstanceOf extends Matcher { | |
191 | |
192 Type _expected_type; | |
Bob Nystrom
2012/05/30 23:23:51
"_expectedType"
gram
2012/06/01 17:33:15
No longer applicable.
| |
193 | |
194 _IsInstanceOf(Type this._expected_type) { | |
195 if (!(_expected_type is Type)) { | |
196 throw new IllegalArgumentException('IsInstanceOf requires type'); | |
197 } | |
198 } | |
199 | |
200 bool matches(item) => _expected_type.isInstance(item); | |
201 | |
202 // The description here is lame :-( | |
Bob Nystrom
2012/05/30 23:23:51
Why?
gram
2012/06/01 17:33:15
Because we don't have the type name unless it is e
| |
203 IDescription describe(IDescription description) => | |
204 description.append('an instance of ${_expected_type.toString()}'); | |
Bob Nystrom
2012/05/30 23:23:51
You don't need .toString() here. Interpolation doe
gram
2012/06/01 17:33:15
Done.
| |
205 } | |
206 | |
207 /** | |
208 * Returns a matcher that matches if an object has a length property | |
209 * that matches [matcher]. | |
Bob Nystrom
2012/05/30 23:23:51
This doesn't seem to add a lot of value. Why not j
gram
2012/06/01 17:33:15
You can make this a bit shorter, with:
expect(foo
Bob Nystrom
2012/06/01 18:22:22
I like terseness, but I also like simplicity and m
| |
210 */ | |
211 IMatcher hasLength([matcher = null]) => | |
212 new _HasLength(matcher == null ? anything() : wrapMatcher(matcher)); | |
213 | |
214 // TODO(gram) - this is intended to fail if the value | |
215 // does not have a length property but it doesn't. See if this | |
216 // can be fixed. | |
217 class _HasLength extends Matcher { | |
218 | |
219 var _len_matcher; | |
Bob Nystrom
2012/05/30 23:23:51
"_lenMatcher"
gram
2012/06/01 17:33:15
Done.
| |
220 | |
221 _HasLength(Matcher this._len_matcher) { } | |
222 bool matches(item) { | |
223 try { | |
224 // TODO(gram): this hack to throw if no length property is not | |
225 // working | |
226 if ((item.length * item.length) >= 0) { | |
227 return _len_matcher.matches(item.length); | |
228 } | |
229 } catch(var e) { | |
230 return false; | |
231 } | |
232 } | |
233 | |
234 IDescription describeMismatch(item, IDescription mismatchDescription) { | |
235 super.describeMismatch(item, mismatchDescription); | |
236 try { | |
237 // TODO(gram): this hack to throw if no length property is not | |
238 // working | |
239 if ((item.length * item.length) >= 0) { | |
240 return mismatchDescription.append(' with length of '). | |
241 appendDescriptionOf(item.length); | |
242 } | |
243 } catch (var e) { | |
244 return mismatchDescription.append(' has no length property'); | |
245 } | |
246 } | |
247 | |
248 IDescription describe(IDescription description) => | |
249 description.append('an object with length of '). | |
250 appendDescriptionOf(_len_matcher); | |
251 } | |
252 | |
253 /** Returns a matcher that matches if value.toString() matches [matcher] */ | |
254 IMatcher hasString(matcher) => new _HasString(wrapMatcher(matcher)); | |
Bob Nystrom
2012/05/30 23:23:51
This name is confusing to me, and it doesn't seem
gram
2012/06/01 17:33:15
Done.
| |
255 | |
256 class _HasString extends Matcher { | |
257 var _str_matcher; | |
258 | |
259 _HasString(this._str_matcher); | |
260 | |
261 bool matches(item) => _str_matcher.matches(item.toString()); | |
262 | |
263 IDescription describe(IDescription description) => | |
264 description.append('with toString() value '). | |
265 appendDescriptionOf(_str_matcher); | |
266 } | |
267 | |
268 /** | |
269 * Returns a matcher that does a deep recursive match. This only works | |
270 * with built-in types. Note that cyclic structures will never terminate! | |
Bob Nystrom
2012/05/30 23:23:51
Wouldn't this also work with user-defined types th
gram
2012/06/01 17:33:15
I made it a bit more general, and added an item co
| |
271 */ | |
272 IMatcher recursivelyMatches(obj) => new _DeepMatcher(obj); | |
273 | |
274 class _DeepMatcher extends Matcher { | |
275 var _obj; | |
276 | |
277 _DeepMatcher(this._obj) {} | |
278 | |
279 String _recursiveMatch(expected, actual, String location) { | |
280 String reason = null; | |
281 if (expected is Collection) { | |
282 if (!(actual is Collection)) { | |
283 reason = 'expected a collection'; | |
284 } else if (expected.length != actual.length) { | |
285 reason = 'different collection lengths'; | |
286 } else { | |
287 for (var i = 0; i < expected.length; i++) { | |
288 reason = _recursiveMatch(expected[i], actual[i], | |
289 'at position ${i} ${location}'); | |
Bob Nystrom
2012/05/30 23:23:51
Indent another 2 spaces. Continued lines are inden
gram
2012/06/01 17:33:15
Done.
| |
290 if (reason != null) { | |
291 break; | |
292 } | |
293 } | |
294 } | |
295 } else if (expected is Map) { | |
296 if (!(actual is Map)) { | |
Bob Nystrom
2012/05/30 23:23:51
actual is !Map
gram
2012/06/01 17:33:15
Done.
| |
297 reason = 'expected a map'; | |
298 } else if (expected.length != actual.length) { | |
299 reason = 'different map lengths'; | |
300 } else { | |
301 for (var key in expected.getKeys()) { | |
302 if (!actual.containsKey(key)) { | |
303 reason = 'missing map key ${key}'; | |
304 break; | |
305 } | |
306 reason = _recursiveMatch(expected[key], actual[key], | |
307 'with key ${key} ${location}'); | |
308 if (reason != null) { | |
309 break; | |
310 } | |
311 } | |
312 } | |
313 } else { | |
314 if (expected != actual) { | |
315 reason = 'expected ${expected} but got ${actual}'; | |
316 } | |
317 } | |
318 if (reason == null) { | |
319 return null; | |
320 } else { | |
321 return '${reason} ${location}'; | |
322 } | |
323 } | |
324 | |
325 bool matches(item) => (_recursiveMatch(_obj, item, '') == null); | |
326 | |
327 IDescription describe(IDescription description) => | |
328 description.append('recursively matches '). | |
329 appendDescriptionOf(_obj); | |
330 | |
331 | |
332 IDescription describeMismatch(item, IDescription mismatchDescription) => | |
333 mismatchDescription.append(_recursiveMatch(_obj, item, '')); | |
334 } | |
335 | |
336 | |
337 | |
OLD | NEW |