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

Side by Side Diff: lib/unittest/core_matchers.dart

Issue 10832058: Improved the way we generate mismatch descriptions. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 4 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
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 5
6 /** 6 /**
7 * Returns a matcher that matches empty strings, maps or collections. 7 * Returns a matcher that matches empty strings, maps or collections.
8 */ 8 */
9 final Matcher isEmpty = const _Empty(); 9 final Matcher isEmpty = const _Empty();
10 10
11 class _Empty extends BaseMatcher { 11 class _Empty extends BaseMatcher {
12 const _Empty(); 12 const _Empty();
13 bool matches(item) { 13 bool matches(item, MatchState matchState) {
14 if (item is Map || item is Collection) { 14 if (item is Map || item is Collection) {
15 return item.isEmpty(); 15 return item.isEmpty();
16 } else if (item is String) { 16 } else if (item is String) {
17 return item.length == 0; 17 return item.length == 0;
18 } else { 18 } else {
19 return false; 19 return false;
20 } 20 }
21 } 21 }
22 Description describe(Description description) => 22 Description describe(Description description) =>
23 description.add('empty'); 23 description.add('empty');
24 } 24 }
25 25
26 /** A matcher that matches any null value. */ 26 /** A matcher that matches any null value. */
27 final Matcher isNull = const _IsNull(); 27 final Matcher isNull = const _IsNull();
28 28
29 /** A matcher that matches any non-null value. */ 29 /** A matcher that matches any non-null value. */
30 final Matcher isNotNull = const _IsNotNull(); 30 final Matcher isNotNull = const _IsNotNull();
31 31
32 class _IsNull extends BaseMatcher { 32 class _IsNull extends BaseMatcher {
33 const _IsNull(); 33 const _IsNull();
34 bool matches(item) => item == null; 34 bool matches(item, MatchState matchState) => item == null;
35 Description describe(Description description) => 35 Description describe(Description description) =>
36 description.add('null'); 36 description.add('null');
37 } 37 }
38 38
39 class _IsNotNull extends BaseMatcher { 39 class _IsNotNull extends BaseMatcher {
40 const _IsNotNull(); 40 const _IsNotNull();
41 bool matches(item) => item != null; 41 bool matches(item, MatchState matchState) => item != null;
42 Description describe(Description description) => 42 Description describe(Description description) =>
43 description.add('not null'); 43 description.add('not null');
44 } 44 }
45 45
46 /** A matcher that matches the Boolean value true. */ 46 /** A matcher that matches the Boolean value true. */
47 final Matcher isTrue = const _IsTrue(); 47 final Matcher isTrue = const _IsTrue();
48 48
49 /** A matcher that matches anything except the Boolean value true. */ 49 /** A matcher that matches anything except the Boolean value true. */
50 final Matcher isFalse = const _IsFalse(); 50 final Matcher isFalse = const _IsFalse();
51 51
52 class _IsTrue extends BaseMatcher { 52 class _IsTrue extends BaseMatcher {
53 const _IsTrue(); 53 const _IsTrue();
54 bool matches(item) => item == true; 54 bool matches(item, MatchState matchState) => item == true;
55 Description describe(Description description) => 55 Description describe(Description description) =>
56 description.add('true'); 56 description.add('true');
57 } 57 }
58 58
59 class _IsFalse extends BaseMatcher { 59 class _IsFalse extends BaseMatcher {
60 const _IsFalse(); 60 const _IsFalse();
61 bool matches(item) => item != true; 61 bool matches(item, MatchState matchState) => item != true;
62 Description describe(Description description) => 62 Description describe(Description description) =>
63 description.add('false'); 63 description.add('false');
64 } 64 }
65 65
66 /** 66 /**
67 * Returns a matches that matches if the value is the same instance 67 * Returns a matches that matches if the value is the same instance
68 * as [object] (`===`). 68 * as [object] (`===`).
69 */ 69 */
70 Matcher same(expected) => new _IsSameAs(expected); 70 Matcher same(expected) => new _IsSameAs(expected);
71 71
72 class _IsSameAs extends BaseMatcher { 72 class _IsSameAs extends BaseMatcher {
73 final _expected; 73 final _expected;
74 const _IsSameAs(this._expected); 74 const _IsSameAs(this._expected);
75 bool matches(item) => item === _expected; 75 bool matches(item, MatchState matchState) => item === _expected;
76 // If all types were hashable we could show a hash here. 76 // If all types were hashable we could show a hash here.
77 Description describe(Description description) => 77 Description describe(Description description) =>
78 description.add('same instance as ').addDescriptionOf(_expected); 78 description.add('same instance as ').addDescriptionOf(_expected);
79 } 79 }
80 80
81 /** 81 /**
82 * Returns a matcher that does a deep recursive match. This only works 82 * Returns a matcher that does a deep recursive match. This only works
83 * with scalars, Maps and Iterables. To handle cyclic structures a 83 * with scalars, Maps and Iterables. To handle cyclic structures a
84 * recursion depth [limit] can be provided. The default limit is 100. 84 * recursion depth [limit] can be provided. The default limit is 100.
85 */ 85 */
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 reason.add(' ').add(location); 170 reason.add(' ').add(location);
171 } 171 }
172 return reason; 172 return reason;
173 } 173 }
174 174
175 String _match(expected, actual) { 175 String _match(expected, actual) {
176 Description reason = _recursiveMatch(expected, actual, '', 0); 176 Description reason = _recursiveMatch(expected, actual, '', 0);
177 return reason == null ? null : reason.toString(); 177 return reason == null ? null : reason.toString();
178 } 178 }
179 179
180 bool matches(item) => _match(_expected, item) == null; 180 // TODO(gram) - see if we can make use of matchState here to avoid
181 // recursing again in describeMismatch.
182 bool matches(item, MatchState matchState) => _match(_expected, item) == null;
181 183
182 Description describe(Description description) => 184 Description describe(Description description) =>
183 description.addDescriptionOf(_expected); 185 description.addDescriptionOf(_expected);
184 186
185 Description describeMismatch(item, Description mismatchDescription) => 187 Description describeMismatch(item, Description mismatchDescription,
188 MatchState matchState, bool verbose) =>
186 mismatchDescription.add(_match(_expected, item)); 189 mismatchDescription.add(_match(_expected, item));
187 } 190 }
188 191
189 /** A matcher that matches any value. */ 192 /** A matcher that matches any value. */
190 final Matcher anything = const _IsAnything(); 193 final Matcher anything = const _IsAnything();
191 194
192 class _IsAnything extends BaseMatcher { 195 class _IsAnything extends BaseMatcher {
193 const _IsAnything(); 196 const _IsAnything();
194 bool matches(item) => true; 197 bool matches(item, MatchState matchState) => true;
195 Description describe(Description description) => 198 Description describe(Description description) =>
196 description.add('anything'); 199 description.add('anything');
197 } 200 }
198 201
199 /** 202 /**
200 * Returns a matcher that matches if an object is an instance 203 * Returns a matcher that matches if an object is an instance
201 * of [type] (or a subtype). 204 * of [type] (or a subtype).
202 * 205 *
203 * As types are not first class objects in Dart we can only 206 * As types are not first class objects in Dart we can only
204 * approximate this test by using a generic wrapper class. 207 * approximate this test by using a generic wrapper class.
205 * 208 *
206 * For example, to test whether 'bar' is an instance of type 209 * For example, to test whether 'bar' is an instance of type
207 * 'Foo', we would write: 210 * 'Foo', we would write:
208 * 211 *
209 * expect(bar, new isInstanceOf<Foo>()); 212 * expect(bar, new isInstanceOf<Foo>());
210 * 213 *
211 * To get better error message, supply a name when creating the 214 * To get better error message, supply a name when creating the
212 * Type wrapper; e.g.: 215 * Type wrapper; e.g.:
213 * 216 *
214 * expect(bar, new isInstanceOf<Foo>('Foo')); 217 * expect(bar, new isInstanceOf<Foo>('Foo'));
215 */ 218 */
216 class isInstanceOf<T> extends BaseMatcher { 219 class isInstanceOf<T> extends BaseMatcher {
217 final String _name; 220 final String _name;
218 const isInstanceOf([name = 'specified type']) : this._name = name; 221 const isInstanceOf([name = 'specified type']) : this._name = name;
219 bool matches(obj) => obj is T; 222 bool matches(obj, MatchState matchState) => obj is T;
220 // The description here is lame :-( 223 // The description here is lame :-(
221 Description describe(Description description) => 224 Description describe(Description description) =>
222 description.add('an instance of ${_name}'); 225 description.add('an instance of ${_name}');
223 } 226 }
224 227
225 /** 228 /**
226 * This can be used to match two kinds of objects: 229 * This can be used to match two kinds of objects:
227 * 230 *
228 * * A [Function] that throws an exception when called. The function cannot 231 * * A [Function] that throws an exception when called. The function cannot
229 * take any arguments. If you want to test that a function expecting 232 * take any arguments. If you want to test that a function expecting
230 * arguments throws, wrap it in another zero-argument function that calls 233 * arguments throws, wrap it in another zero-argument function that calls
231 * the one you want to test. The function will be called once upon success, 234 * the one you want to test. The function will be called once upon success,
232 * or twice upon failure (the second time to get the failure description). 235 * or twice upon failure (the second time to get the failure description).
Siggi Cherem (dart-lang) 2012/07/31 17:16:40 Regarding this part - is it still true that we cal
gram 2012/07/31 17:56:01 Actually the double call was removed some time bac
Siggi Cherem (dart-lang) 2012/07/31 17:59:43 Cool, let's fix the comment then :)
233 * 236 *
234 * * A [Future] that completes with an exception. Note that this creates an 237 * * A [Future] that completes with an exception. Note that this creates an
235 * asynchronous expectation. The call to `expect()` that includes this will 238 * asynchronous expectation. The call to `expect()` that includes this will
236 * return immediately and execution will continue. Later, when the future 239 * return immediately and execution will continue. Later, when the future
237 * completes, the actual expectation will run. 240 * completes, the actual expectation will run.
238 */ 241 */
239 final Matcher throws = const _Throws(); 242 final Matcher throws = const _Throws();
240 243
241 /** 244 /**
242 * This can be used to match two kinds of objects: 245 * This can be used to match two kinds of objects:
(...skipping 19 matching lines...) Expand all
262 * The function will be called once. Any exceptions will be silently swallowed. 265 * The function will be called once. Any exceptions will be silently swallowed.
263 * The value passed to expect() should be a reference to the function. 266 * The value passed to expect() should be a reference to the function.
264 * Note that the function cannot take arguments; to handle this 267 * Note that the function cannot take arguments; to handle this
265 * a wrapper will have to be created. 268 * a wrapper will have to be created.
266 */ 269 */
267 final Matcher returnsNormally = const _ReturnsNormally(); 270 final Matcher returnsNormally = const _ReturnsNormally();
268 271
269 class _Throws extends BaseMatcher { 272 class _Throws extends BaseMatcher {
270 final Matcher _matcher; 273 final Matcher _matcher;
271 274
272 const _Throws([Matcher matcher = null]) : this._matcher = matcher; 275 const _Throws([Matcher matcher]) :
276 this._matcher = matcher;
273 277
274 bool matches(item) { 278 bool matches(item, MatchState matchState) {
275 if (item is Future) { 279 if (item is Future) {
276 // Queue up an asynchronous expectation that validates when the future 280 // Queue up an asynchronous expectation that validates when the future
277 // completes. 281 // completes.
278 item.onComplete(expectAsync1((future) { 282 item.onComplete(expectAsync1((future) {
279 if (future.hasValue) { 283 if (future.hasValue) {
280 expect(false, reason: 284 expect(false, reason:
281 "Expected future to fail, but succeeded with '${future.value}'."); 285 "Expected future to fail, but succeeded with '${future.value}'.");
282 } else if (_matcher != null) { 286 } else if (_matcher != null) {
283 var reason; 287 var reason;
284 if (future.stackTrace != null) { 288 if (future.stackTrace != null) {
285 var stackTrace = future.stackTrace.toString(); 289 var stackTrace = future.stackTrace.toString();
286 stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}"; 290 stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}";
287 reason = "Actual exception trace:\n$stackTrace"; 291 reason = "Actual exception trace:\n$stackTrace";
288 } 292 }
289 expect(future.exception, _matcher, reason: reason); 293 expect(future.exception, _matcher, reason: reason);
290 } 294 }
291 })); 295 }));
292 296
293 // It hasn't failed yet. 297 // It hasn't failed yet.
294 return true; 298 return true;
295 } 299 }
296 300
297 try { 301 try {
298 item(); 302 item();
299 return false; 303 return false;
300 } catch (final e) { 304 } catch (final e, final s) {
301 return _matcher == null || _matcher.matches(e); 305 if (_matcher == null) {
306 return true;
307 } else if (_matcher.matches(e, matchState)) {
Siggi Cherem (dart-lang) 2012/07/31 17:16:40 seems you can keep it as if (_matcher == null || _
gram 2012/07/31 17:56:01 Done.
308 return true;
309 } else {
310 matchState.state = {
311 'exception' :e,
312 'stack': s
313 };
314 return false;
315 }
302 } 316 }
303 } 317 }
304 318
305 Description describe(Description description) { 319 Description describe(Description description) {
306 if (_matcher == null) { 320 if (_matcher == null) {
307 return description.add("throws an exception"); 321 return description.add("throws an exception");
308 } else { 322 } else {
309 return description.add('throws an exception which matches '). 323 return description.add('throws an exception which matches ').
310 addDescriptionOf(_matcher); 324 addDescriptionOf(_matcher);
311 } 325 }
312 } 326 }
313 327
314 Description describeMismatch(item, Description mismatchDescription) { 328 Description describeMismatch(item, Description mismatchDescription,
315 if (_matcher == null) { 329 MatchState matchState,
330 bool verbose) {
331 if (_matcher == null || matchState.state == null) {
316 return mismatchDescription.add(' no exception'); 332 return mismatchDescription.add(' no exception');
317 } else { 333 } else {
318 return mismatchDescription. 334 mismatchDescription.
319 add(' no exception or exception does not match '). 335 add(' exception ').addDescriptionOf(matchState.state['exception']);
320 addDescriptionOf(_matcher); 336 if (verbose) {
337 mismatchDescription.add(' at ').
338 add(matchState.state['stack'].toString());
339 }
340 mismatchDescription.add(' does not match ').addDescriptionOf(_matcher);
341 return mismatchDescription;
321 } 342 }
322 } 343 }
323 } 344 }
324 345
325 class _ReturnsNormally extends BaseMatcher { 346 class _ReturnsNormally extends BaseMatcher {
326
327 const _ReturnsNormally(); 347 const _ReturnsNormally();
328 348
329 bool matches(f) { 349 bool matches(f, MatchState matchState) {
330 try { 350 try {
331 f(); 351 f();
332 return true; 352 return true;
333 } catch (final e) { 353 } catch (final e, final s) {
354 matchState.state = {
355 'exception' : e,
356 'stack': s
357 };
334 return false; 358 return false;
335 } 359 }
336 } 360 }
337 361
338 Description describe(Description description) => 362 Description describe(Description description) =>
339 description.add("return normally"); 363 description.add("return normally");
340 364
341 Description describeMismatch(item, Description mismatchDescription) { 365 Description describeMismatch(item, Description mismatchDescription,
342 return mismatchDescription.add(' threw exception'); 366 MatchState matchState,
367 bool verbose) {
368 mismatchDescription.add(' threw ').
369 addDescriptionOf(matchState.state['exception']);
370 if (verbose) {
371 mismatchDescription.add(' at ').
372 add(matchState.state['stack'].toString());
373 }
374 return mismatchDescription;
343 } 375 }
344 } 376 }
345 377
346 /* 378 /*
347 * Matchers for different exception types. Ideally we should just be able to 379 * Matchers for different exception types. Ideally we should just be able to
348 * use something like: 380 * use something like:
349 * 381 *
350 * final Matcher throwsException = 382 * final Matcher throwsException =
351 * const _Throws(const isInstanceOf<Exception>()); 383 * const _Throws(const isInstanceOf<Exception>());
352 * 384 *
(...skipping 20 matching lines...) Expand all
373 405
374 /** A matcher for BadNumberFormatExceptions. */ 406 /** A matcher for BadNumberFormatExceptions. */
375 final isBadNumberFormatException = const _BadNumberFormatException(); 407 final isBadNumberFormatException = const _BadNumberFormatException();
376 408
377 /** A matcher for functions that throw BadNumberFormatException */ 409 /** A matcher for functions that throw BadNumberFormatException */
378 final Matcher throwsBadNumberFormatException = 410 final Matcher throwsBadNumberFormatException =
379 const _Throws(isBadNumberFormatException); 411 const _Throws(isBadNumberFormatException);
380 412
381 class _BadNumberFormatException extends _ExceptionMatcher { 413 class _BadNumberFormatException extends _ExceptionMatcher {
382 const _BadNumberFormatException() : super("BadNumberFormatException"); 414 const _BadNumberFormatException() : super("BadNumberFormatException");
383 bool matches(item) => item is BadNumberFormatException; 415 bool matches(item, MatchState matchState) => item is BadNumberFormatException;
384 } 416 }
385 417
386 /** A matcher for Exceptions. */ 418 /** A matcher for Exceptions. */
387 final isException = const _Exception(); 419 final isException = const _Exception();
388 420
389 /** A matcher for functions that throw Exception */ 421 /** A matcher for functions that throw Exception */
390 final Matcher throwsException = const _Throws(isException); 422 final Matcher throwsException = const _Throws(isException);
391 423
392 class _Exception extends _ExceptionMatcher { 424 class _Exception extends _ExceptionMatcher {
393 const _Exception() : super("Exception"); 425 const _Exception() : super("Exception");
394 bool matches(item) => item is Exception; 426 bool matches(item, MatchState matchState) => item is Exception;
395 } 427 }
396 428
397 /** A matcher for IllegalArgumentExceptions. */ 429 /** A matcher for IllegalArgumentExceptions. */
398 final isIllegalArgumentException = const _IllegalArgumentException(); 430 final isIllegalArgumentException = const _IllegalArgumentException();
399 431
400 /** A matcher for functions that throw IllegalArgumentException */ 432 /** A matcher for functions that throw IllegalArgumentException */
401 final Matcher throwsIllegalArgumentException = 433 final Matcher throwsIllegalArgumentException =
402 const _Throws(isIllegalArgumentException); 434 const _Throws(isIllegalArgumentException);
403 435
404 class _IllegalArgumentException extends _ExceptionMatcher { 436 class _IllegalArgumentException extends _ExceptionMatcher {
405 const _IllegalArgumentException() : super("IllegalArgumentException"); 437 const _IllegalArgumentException() : super("IllegalArgumentException");
406 bool matches(item) => item is IllegalArgumentException; 438 bool matches(item, MatchState matchState) => item is IllegalArgumentException;
407 } 439 }
408 440
409 /** A matcher for IllegalJSRegExpExceptions. */ 441 /** A matcher for IllegalJSRegExpExceptions. */
410 final isIllegalJSRegExpException = const _IllegalJSRegExpException(); 442 final isIllegalJSRegExpException = const _IllegalJSRegExpException();
411 443
412 /** A matcher for functions that throw IllegalJSRegExpException */ 444 /** A matcher for functions that throw IllegalJSRegExpException */
413 final Matcher throwsIllegalJSRegExpException = 445 final Matcher throwsIllegalJSRegExpException =
414 const _Throws(isIllegalJSRegExpException); 446 const _Throws(isIllegalJSRegExpException);
415 447
416 class _IllegalJSRegExpException extends _ExceptionMatcher { 448 class _IllegalJSRegExpException extends _ExceptionMatcher {
417 const _IllegalJSRegExpException() : super("IllegalJSRegExpException"); 449 const _IllegalJSRegExpException() : super("IllegalJSRegExpException");
418 bool matches(item) => item is IllegalJSRegExpException; 450 bool matches(item, MatchState matchState) => item is IllegalJSRegExpException;
419 } 451 }
420 452
421 /** A matcher for IndexOutOfRangeExceptions. */ 453 /** A matcher for IndexOutOfRangeExceptions. */
422 final isIndexOutOfRangeException = const _IndexOutOfRangeException(); 454 final isIndexOutOfRangeException = const _IndexOutOfRangeException();
423 455
424 /** A matcher for functions that throw IndexOutOfRangeException */ 456 /** A matcher for functions that throw IndexOutOfRangeException */
425 final Matcher throwsIndexOutOfRangeException = 457 final Matcher throwsIndexOutOfRangeException =
426 const _Throws(isIndexOutOfRangeException); 458 const _Throws(isIndexOutOfRangeException);
427 459
428 class _IndexOutOfRangeException extends _ExceptionMatcher { 460 class _IndexOutOfRangeException extends _ExceptionMatcher {
429 const _IndexOutOfRangeException() : super("IndexOutOfRangeException"); 461 const _IndexOutOfRangeException() : super("IndexOutOfRangeException");
430 bool matches(item) => item is IndexOutOfRangeException; 462 bool matches(item, MatchState matchState) => item is IndexOutOfRangeException;
431 } 463 }
432 464
433 /** A matcher for NoSuchMethodExceptions. */ 465 /** A matcher for NoSuchMethodExceptions. */
434 final isNoSuchMethodException = const _NoSuchMethodException(); 466 final isNoSuchMethodException = const _NoSuchMethodException();
435 467
436 /** A matcher for functions that throw NoSuchMethodException */ 468 /** A matcher for functions that throw NoSuchMethodException */
437 final Matcher throwsNoSuchMethodException = 469 final Matcher throwsNoSuchMethodException =
438 const _Throws(isNoSuchMethodException); 470 const _Throws(isNoSuchMethodException);
439 471
440 class _NoSuchMethodException extends _ExceptionMatcher { 472 class _NoSuchMethodException extends _ExceptionMatcher {
441 const _NoSuchMethodException() : super("NoSuchMethodException"); 473 const _NoSuchMethodException() : super("NoSuchMethodException");
442 bool matches(item) => item is NoSuchMethodException; 474 bool matches(item, MatchState matchState) => item is NoSuchMethodException;
443 } 475 }
444 476
445 /** A matcher for NotImplementedExceptions. */ 477 /** A matcher for NotImplementedExceptions. */
446 final isNotImplementedException = const _NotImplementedException(); 478 final isNotImplementedException = const _NotImplementedException();
447 479
448 /** A matcher for functions that throw Exception */ 480 /** A matcher for functions that throw Exception */
449 final Matcher throwsNotImplementedException = 481 final Matcher throwsNotImplementedException =
450 const _Throws(isNotImplementedException); 482 const _Throws(isNotImplementedException);
451 483
452 class _NotImplementedException extends _ExceptionMatcher { 484 class _NotImplementedException extends _ExceptionMatcher {
453 const _NotImplementedException() : super("NotImplementedException"); 485 const _NotImplementedException() : super("NotImplementedException");
454 bool matches(item) => item is NotImplementedException; 486 bool matches(item, MatchState matchState) => item is NotImplementedException;
455 } 487 }
456 488
457 /** A matcher for NullPointerExceptions. */ 489 /** A matcher for NullPointerExceptions. */
458 final isNullPointerException = const _NullPointerException(); 490 final isNullPointerException = const _NullPointerException();
459 491
460 /** A matcher for functions that throw NotNullPointerException */ 492 /** A matcher for functions that throw NotNullPointerException */
461 final Matcher throwsNullPointerException = 493 final Matcher throwsNullPointerException =
462 const _Throws(isNullPointerException); 494 const _Throws(isNullPointerException);
463 495
464 class _NullPointerException extends _ExceptionMatcher { 496 class _NullPointerException extends _ExceptionMatcher {
465 const _NullPointerException() : super("NullPointerException"); 497 const _NullPointerException() : super("NullPointerException");
466 bool matches(item) => item is NullPointerException; 498 bool matches(item, MatchState matchState) => item is NullPointerException;
467 } 499 }
468 500
469 /** A matcher for UnsupportedOperationExceptions. */ 501 /** A matcher for UnsupportedOperationExceptions. */
470 final isUnsupportedOperationException = const _UnsupportedOperationException(); 502 final isUnsupportedOperationException = const _UnsupportedOperationException();
471 503
472 /** A matcher for functions that throw UnsupportedOperationException */ 504 /** A matcher for functions that throw UnsupportedOperationException */
473 final Matcher throwsUnsupportedOperationException = 505 final Matcher throwsUnsupportedOperationException =
474 const _Throws(isUnsupportedOperationException); 506 const _Throws(isUnsupportedOperationException);
475 507
476 class _UnsupportedOperationException extends _ExceptionMatcher { 508 class _UnsupportedOperationException extends _ExceptionMatcher {
477 const _UnsupportedOperationException() : 509 const _UnsupportedOperationException() :
478 super("UnsupportedOperationException"); 510 super("UnsupportedOperationException");
479 bool matches(item) => item is UnsupportedOperationException; 511 bool matches(item, MatchState matchState) => item is UnsupportedOperationExcep tion;
480 } 512 }
481 513
482 /** 514 /**
483 * Returns a matcher that matches if an object has a length property 515 * Returns a matcher that matches if an object has a length property
484 * that matches [matcher]. 516 * that matches [matcher].
485 */ 517 */
486 Matcher hasLength(matcher) => 518 Matcher hasLength(matcher) =>
487 new _HasLength(wrapMatcher(matcher)); 519 new _HasLength(wrapMatcher(matcher));
488 520
489 class _HasLength extends BaseMatcher { 521 class _HasLength extends BaseMatcher {
490 final Matcher _matcher; 522 final Matcher _matcher;
491 const _HasLength([Matcher matcher = null]) : this._matcher = matcher; 523 const _HasLength([Matcher matcher = null]) : this._matcher = matcher;
492 524
493 bool matches(item) { 525 bool matches(item, MatchState matchState) {
494 return _matcher.matches(item.length); 526 return _matcher.matches(item.length, matchState);
495 } 527 }
496 528
497 Description describe(Description description) => 529 Description describe(Description description) =>
498 description.add('an object with length of '). 530 description.add('an object with length of ').
499 addDescriptionOf(_matcher); 531 addDescriptionOf(_matcher);
500 532
501 Description describeMismatch(item, Description mismatchDescription) { 533 Description describeMismatch(item, Description mismatchDescription,
502 super.describeMismatch(item, mismatchDescription); 534 MatchState matchState, bool verbose) {
535 super.describeMismatch(item, mismatchDescription, matchState, verbose);
503 try { 536 try {
504 // We want to generate a different description if there is no length 537 // We want to generate a different description if there is no length
505 // property. This is harmless code that will throw if no length property 538 // property. This is harmless code that will throw if no length property
506 // but subtle enough that an optimizer shouldn't strip it out. 539 // but subtle enough that an optimizer shouldn't strip it out.
507 if (item.length * item.length >= 0) { 540 if (item.length * item.length >= 0) {
508 return mismatchDescription.add(' with length of '). 541 return mismatchDescription.add(' with length of ').
509 addDescriptionOf(item.length); 542 addDescriptionOf(item.length);
510 } 543 }
511 } catch (var e) { 544 } catch (var e) {
512 return mismatchDescription.add(' has no length property'); 545 return mismatchDescription.add(' has no length property');
513 } 546 }
514 } 547 }
515 } 548 }
516 549
517 /** 550 /**
518 * Returns a matcher that matches if the match argument contains 551 * Returns a matcher that matches if the match argument contains
519 * the expected value. For [String]s this means substring matching; 552 * the expected value. For [String]s this means substring matching;
520 * for [Map]s is means the map has the key, and for [Collection]s it 553 * for [Map]s is means the map has the key, and for [Collection]s it
521 * means the collection has a matching element. In the case of collections, 554 * means the collection has a matching element. In the case of collections,
522 * [expected] can itself be a matcher. 555 * [expected] can itself be a matcher.
523 */ 556 */
524 Matcher contains(expected) => new _Contains(expected); 557 Matcher contains(expected) => new _Contains(expected);
525 558
526 class _Contains extends BaseMatcher { 559 class _Contains extends BaseMatcher {
527 560
528 final _expected; 561 final _expected;
529 562
530 const _Contains(this._expected); 563 const _Contains(this._expected);
531 564
532 bool matches(item) { 565 bool matches(item, MatchState matchState) {
533 if (item is String) { 566 if (item is String) {
534 return item.indexOf(_expected) >= 0; 567 return item.indexOf(_expected) >= 0;
535 } else if (item is Collection) { 568 } else if (item is Collection) {
536 if (_expected is Matcher) { 569 if (_expected is Matcher) {
537 return item.some((e) => _expected.matches(e)); 570 return item.some((e) => _expected.matches(e, matchState));
538 } else { 571 } else {
539 return item.some((e) => e == _expected); 572 return item.some((e) => e == _expected);
540 } 573 }
541 } else if (item is Map) { 574 } else if (item is Map) {
542 return item.containsKey(_expected); 575 return item.containsKey(_expected);
543 } 576 }
544 return false; 577 return false;
545 } 578 }
546 579
547 Description describe(Description description) => 580 Description describe(Description description) =>
548 description.add('contains ').addDescriptionOf(_expected); 581 description.add('contains ').addDescriptionOf(_expected);
549 } 582 }
550 583
551 /** 584 /**
552 * Returns a matcher that matches if the match argument is in 585 * Returns a matcher that matches if the match argument is in
553 * the expected value. This is the converse of [contains]. 586 * the expected value. This is the converse of [contains].
554 */ 587 */
555 Matcher isIn(expected) => new _In(expected); 588 Matcher isIn(expected) => new _In(expected);
556 589
557 class _In extends BaseMatcher { 590 class _In extends BaseMatcher {
558 591
559 final _expected; 592 final _expected;
560 593
561 const _In(this._expected); 594 const _In(this._expected);
562 595
563 bool matches(item) { 596 bool matches(item, MatchState matchState) {
564 if (_expected is String) { 597 if (_expected is String) {
565 return _expected.indexOf(item) >= 0; 598 return _expected.indexOf(item) >= 0;
566 } else if (_expected is Collection) { 599 } else if (_expected is Collection) {
567 return _expected.some((e) => e == item); 600 return _expected.some((e) => e == item);
568 } else if (_expected is Map) { 601 } else if (_expected is Map) {
569 return _expected.containsKey(item); 602 return _expected.containsKey(item);
570 } 603 }
571 return false; 604 return false;
572 } 605 }
573 606
574 Description describe(Description description) => 607 Description describe(Description description) =>
575 description.add('is in ').addDescriptionOf(_expected); 608 description.add('is in ').addDescriptionOf(_expected);
576 } 609 }
577 610
578 /** 611 /**
579 * Returns a matcher that uses an arbitrary function that returns 612 * Returns a matcher that uses an arbitrary function that returns
580 * true or false for the actual value. 613 * true or false for the actual value.
581 */ 614 */
582 Matcher predicate(f, [description = 'satisfies function']) => 615 Matcher predicate(f, [description = 'satisfies function']) =>
583 new _Predicate(f, description); 616 new _Predicate(f, description);
584 617
585 class _Predicate extends BaseMatcher { 618 class _Predicate extends BaseMatcher {
586 619
587 final _matcher; 620 final _matcher;
588 final String _description; 621 final String _description;
589 622
590 const _Predicate(this._matcher, this._description); 623 const _Predicate(this._matcher, this._description);
591 624
592 bool matches(item) => _matcher(item); 625 bool matches(item, MatchState matchState) => _matcher(item);
593 626
594 Description describe(Description description) => 627 Description describe(Description description) =>
595 description.add(_description); 628 description.add(_description);
596 } 629 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698