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

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
« no previous file with comments | « lib/unittest/collection_matchers.dart ('k') | lib/unittest/expect.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 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
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 ||_matcher.matches(e, matchState)) {
306 return true;
307 } else {
308 matchState.state = {
309 'exception' :e,
310 'stack': s
311 };
312 return false;
313 }
302 } 314 }
303 } 315 }
304 316
305 Description describe(Description description) { 317 Description describe(Description description) {
306 if (_matcher == null) { 318 if (_matcher == null) {
307 return description.add("throws an exception"); 319 return description.add("throws an exception");
308 } else { 320 } else {
309 return description.add('throws an exception which matches '). 321 return description.add('throws an exception which matches ').
310 addDescriptionOf(_matcher); 322 addDescriptionOf(_matcher);
311 } 323 }
312 } 324 }
313 325
314 Description describeMismatch(item, Description mismatchDescription) { 326 Description describeMismatch(item, Description mismatchDescription,
315 if (_matcher == null) { 327 MatchState matchState,
328 bool verbose) {
329 if (_matcher == null || matchState.state == null) {
316 return mismatchDescription.add(' no exception'); 330 return mismatchDescription.add(' no exception');
317 } else { 331 } else {
318 return mismatchDescription. 332 mismatchDescription.
319 add(' no exception or exception does not match '). 333 add(' exception ').addDescriptionOf(matchState.state['exception']);
320 addDescriptionOf(_matcher); 334 if (verbose) {
335 mismatchDescription.add(' at ').
336 add(matchState.state['stack'].toString());
337 }
338 mismatchDescription.add(' does not match ').addDescriptionOf(_matcher);
339 return mismatchDescription;
321 } 340 }
322 } 341 }
323 } 342 }
324 343
325 class _ReturnsNormally extends BaseMatcher { 344 class _ReturnsNormally extends BaseMatcher {
326
327 const _ReturnsNormally(); 345 const _ReturnsNormally();
328 346
329 bool matches(f) { 347 bool matches(f, MatchState matchState) {
330 try { 348 try {
331 f(); 349 f();
332 return true; 350 return true;
333 } catch (final e) { 351 } catch (final e, final s) {
352 matchState.state = {
353 'exception' : e,
354 'stack': s
355 };
334 return false; 356 return false;
335 } 357 }
336 } 358 }
337 359
338 Description describe(Description description) => 360 Description describe(Description description) =>
339 description.add("return normally"); 361 description.add("return normally");
340 362
341 Description describeMismatch(item, Description mismatchDescription) { 363 Description describeMismatch(item, Description mismatchDescription,
342 return mismatchDescription.add(' threw exception'); 364 MatchState matchState,
365 bool verbose) {
366 mismatchDescription.add(' threw ').
367 addDescriptionOf(matchState.state['exception']);
368 if (verbose) {
369 mismatchDescription.add(' at ').
370 add(matchState.state['stack'].toString());
371 }
372 return mismatchDescription;
343 } 373 }
344 } 374 }
345 375
346 /* 376 /*
347 * Matchers for different exception types. Ideally we should just be able to 377 * Matchers for different exception types. Ideally we should just be able to
348 * use something like: 378 * use something like:
349 * 379 *
350 * final Matcher throwsException = 380 * final Matcher throwsException =
351 * const _Throws(const isInstanceOf<Exception>()); 381 * const _Throws(const isInstanceOf<Exception>());
352 * 382 *
(...skipping 20 matching lines...) Expand all
373 403
374 /** A matcher for BadNumberFormatExceptions. */ 404 /** A matcher for BadNumberFormatExceptions. */
375 final isBadNumberFormatException = const _BadNumberFormatException(); 405 final isBadNumberFormatException = const _BadNumberFormatException();
376 406
377 /** A matcher for functions that throw BadNumberFormatException */ 407 /** A matcher for functions that throw BadNumberFormatException */
378 final Matcher throwsBadNumberFormatException = 408 final Matcher throwsBadNumberFormatException =
379 const _Throws(isBadNumberFormatException); 409 const _Throws(isBadNumberFormatException);
380 410
381 class _BadNumberFormatException extends _ExceptionMatcher { 411 class _BadNumberFormatException extends _ExceptionMatcher {
382 const _BadNumberFormatException() : super("BadNumberFormatException"); 412 const _BadNumberFormatException() : super("BadNumberFormatException");
383 bool matches(item) => item is BadNumberFormatException; 413 bool matches(item, MatchState matchState) => item is BadNumberFormatException;
384 } 414 }
385 415
386 /** A matcher for Exceptions. */ 416 /** A matcher for Exceptions. */
387 final isException = const _Exception(); 417 final isException = const _Exception();
388 418
389 /** A matcher for functions that throw Exception */ 419 /** A matcher for functions that throw Exception */
390 final Matcher throwsException = const _Throws(isException); 420 final Matcher throwsException = const _Throws(isException);
391 421
392 class _Exception extends _ExceptionMatcher { 422 class _Exception extends _ExceptionMatcher {
393 const _Exception() : super("Exception"); 423 const _Exception() : super("Exception");
394 bool matches(item) => item is Exception; 424 bool matches(item, MatchState matchState) => item is Exception;
395 } 425 }
396 426
397 /** A matcher for IllegalArgumentExceptions. */ 427 /** A matcher for IllegalArgumentExceptions. */
398 final isIllegalArgumentException = const _IllegalArgumentException(); 428 final isIllegalArgumentException = const _IllegalArgumentException();
399 429
400 /** A matcher for functions that throw IllegalArgumentException */ 430 /** A matcher for functions that throw IllegalArgumentException */
401 final Matcher throwsIllegalArgumentException = 431 final Matcher throwsIllegalArgumentException =
402 const _Throws(isIllegalArgumentException); 432 const _Throws(isIllegalArgumentException);
403 433
404 class _IllegalArgumentException extends _ExceptionMatcher { 434 class _IllegalArgumentException extends _ExceptionMatcher {
405 const _IllegalArgumentException() : super("IllegalArgumentException"); 435 const _IllegalArgumentException() : super("IllegalArgumentException");
406 bool matches(item) => item is IllegalArgumentException; 436 bool matches(item, MatchState matchState) => item is IllegalArgumentException;
407 } 437 }
408 438
409 /** A matcher for IllegalJSRegExpExceptions. */ 439 /** A matcher for IllegalJSRegExpExceptions. */
410 final isIllegalJSRegExpException = const _IllegalJSRegExpException(); 440 final isIllegalJSRegExpException = const _IllegalJSRegExpException();
411 441
412 /** A matcher for functions that throw IllegalJSRegExpException */ 442 /** A matcher for functions that throw IllegalJSRegExpException */
413 final Matcher throwsIllegalJSRegExpException = 443 final Matcher throwsIllegalJSRegExpException =
414 const _Throws(isIllegalJSRegExpException); 444 const _Throws(isIllegalJSRegExpException);
415 445
416 class _IllegalJSRegExpException extends _ExceptionMatcher { 446 class _IllegalJSRegExpException extends _ExceptionMatcher {
417 const _IllegalJSRegExpException() : super("IllegalJSRegExpException"); 447 const _IllegalJSRegExpException() : super("IllegalJSRegExpException");
418 bool matches(item) => item is IllegalJSRegExpException; 448 bool matches(item, MatchState matchState) => item is IllegalJSRegExpException;
419 } 449 }
420 450
421 /** A matcher for IndexOutOfRangeExceptions. */ 451 /** A matcher for IndexOutOfRangeExceptions. */
422 final isIndexOutOfRangeException = const _IndexOutOfRangeException(); 452 final isIndexOutOfRangeException = const _IndexOutOfRangeException();
423 453
424 /** A matcher for functions that throw IndexOutOfRangeException */ 454 /** A matcher for functions that throw IndexOutOfRangeException */
425 final Matcher throwsIndexOutOfRangeException = 455 final Matcher throwsIndexOutOfRangeException =
426 const _Throws(isIndexOutOfRangeException); 456 const _Throws(isIndexOutOfRangeException);
427 457
428 class _IndexOutOfRangeException extends _ExceptionMatcher { 458 class _IndexOutOfRangeException extends _ExceptionMatcher {
429 const _IndexOutOfRangeException() : super("IndexOutOfRangeException"); 459 const _IndexOutOfRangeException() : super("IndexOutOfRangeException");
430 bool matches(item) => item is IndexOutOfRangeException; 460 bool matches(item, MatchState matchState) => item is IndexOutOfRangeException;
431 } 461 }
432 462
433 /** A matcher for NoSuchMethodExceptions. */ 463 /** A matcher for NoSuchMethodExceptions. */
434 final isNoSuchMethodException = const _NoSuchMethodException(); 464 final isNoSuchMethodException = const _NoSuchMethodException();
435 465
436 /** A matcher for functions that throw NoSuchMethodException */ 466 /** A matcher for functions that throw NoSuchMethodException */
437 final Matcher throwsNoSuchMethodException = 467 final Matcher throwsNoSuchMethodException =
438 const _Throws(isNoSuchMethodException); 468 const _Throws(isNoSuchMethodException);
439 469
440 class _NoSuchMethodException extends _ExceptionMatcher { 470 class _NoSuchMethodException extends _ExceptionMatcher {
441 const _NoSuchMethodException() : super("NoSuchMethodException"); 471 const _NoSuchMethodException() : super("NoSuchMethodException");
442 bool matches(item) => item is NoSuchMethodException; 472 bool matches(item, MatchState matchState) => item is NoSuchMethodException;
443 } 473 }
444 474
445 /** A matcher for NotImplementedExceptions. */ 475 /** A matcher for NotImplementedExceptions. */
446 final isNotImplementedException = const _NotImplementedException(); 476 final isNotImplementedException = const _NotImplementedException();
447 477
448 /** A matcher for functions that throw Exception */ 478 /** A matcher for functions that throw Exception */
449 final Matcher throwsNotImplementedException = 479 final Matcher throwsNotImplementedException =
450 const _Throws(isNotImplementedException); 480 const _Throws(isNotImplementedException);
451 481
452 class _NotImplementedException extends _ExceptionMatcher { 482 class _NotImplementedException extends _ExceptionMatcher {
453 const _NotImplementedException() : super("NotImplementedException"); 483 const _NotImplementedException() : super("NotImplementedException");
454 bool matches(item) => item is NotImplementedException; 484 bool matches(item, MatchState matchState) => item is NotImplementedException;
455 } 485 }
456 486
457 /** A matcher for NullPointerExceptions. */ 487 /** A matcher for NullPointerExceptions. */
458 final isNullPointerException = const _NullPointerException(); 488 final isNullPointerException = const _NullPointerException();
459 489
460 /** A matcher for functions that throw NotNullPointerException */ 490 /** A matcher for functions that throw NotNullPointerException */
461 final Matcher throwsNullPointerException = 491 final Matcher throwsNullPointerException =
462 const _Throws(isNullPointerException); 492 const _Throws(isNullPointerException);
463 493
464 class _NullPointerException extends _ExceptionMatcher { 494 class _NullPointerException extends _ExceptionMatcher {
465 const _NullPointerException() : super("NullPointerException"); 495 const _NullPointerException() : super("NullPointerException");
466 bool matches(item) => item is NullPointerException; 496 bool matches(item, MatchState matchState) => item is NullPointerException;
467 } 497 }
468 498
469 /** A matcher for UnsupportedOperationExceptions. */ 499 /** A matcher for UnsupportedOperationExceptions. */
470 final isUnsupportedOperationException = const _UnsupportedOperationException(); 500 final isUnsupportedOperationException = const _UnsupportedOperationException();
471 501
472 /** A matcher for functions that throw UnsupportedOperationException */ 502 /** A matcher for functions that throw UnsupportedOperationException */
473 final Matcher throwsUnsupportedOperationException = 503 final Matcher throwsUnsupportedOperationException =
474 const _Throws(isUnsupportedOperationException); 504 const _Throws(isUnsupportedOperationException);
475 505
476 class _UnsupportedOperationException extends _ExceptionMatcher { 506 class _UnsupportedOperationException extends _ExceptionMatcher {
477 const _UnsupportedOperationException() : 507 const _UnsupportedOperationException() :
478 super("UnsupportedOperationException"); 508 super("UnsupportedOperationException");
479 bool matches(item) => item is UnsupportedOperationException; 509 bool matches(item, MatchState matchState) => item is UnsupportedOperationExcep tion;
480 } 510 }
481 511
482 /** 512 /**
483 * Returns a matcher that matches if an object has a length property 513 * Returns a matcher that matches if an object has a length property
484 * that matches [matcher]. 514 * that matches [matcher].
485 */ 515 */
486 Matcher hasLength(matcher) => 516 Matcher hasLength(matcher) =>
487 new _HasLength(wrapMatcher(matcher)); 517 new _HasLength(wrapMatcher(matcher));
488 518
489 class _HasLength extends BaseMatcher { 519 class _HasLength extends BaseMatcher {
490 final Matcher _matcher; 520 final Matcher _matcher;
491 const _HasLength([Matcher matcher = null]) : this._matcher = matcher; 521 const _HasLength([Matcher matcher = null]) : this._matcher = matcher;
492 522
493 bool matches(item) { 523 bool matches(item, MatchState matchState) {
494 return _matcher.matches(item.length); 524 return _matcher.matches(item.length, matchState);
495 } 525 }
496 526
497 Description describe(Description description) => 527 Description describe(Description description) =>
498 description.add('an object with length of '). 528 description.add('an object with length of ').
499 addDescriptionOf(_matcher); 529 addDescriptionOf(_matcher);
500 530
501 Description describeMismatch(item, Description mismatchDescription) { 531 Description describeMismatch(item, Description mismatchDescription,
502 super.describeMismatch(item, mismatchDescription); 532 MatchState matchState, bool verbose) {
533 super.describeMismatch(item, mismatchDescription, matchState, verbose);
503 try { 534 try {
504 // We want to generate a different description if there is no length 535 // 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 536 // property. This is harmless code that will throw if no length property
506 // but subtle enough that an optimizer shouldn't strip it out. 537 // but subtle enough that an optimizer shouldn't strip it out.
507 if (item.length * item.length >= 0) { 538 if (item.length * item.length >= 0) {
508 return mismatchDescription.add(' with length of '). 539 return mismatchDescription.add(' with length of ').
509 addDescriptionOf(item.length); 540 addDescriptionOf(item.length);
510 } 541 }
511 } catch (var e) { 542 } catch (var e) {
512 return mismatchDescription.add(' has no length property'); 543 return mismatchDescription.add(' has no length property');
513 } 544 }
514 } 545 }
515 } 546 }
516 547
517 /** 548 /**
518 * Returns a matcher that matches if the match argument contains 549 * Returns a matcher that matches if the match argument contains
519 * the expected value. For [String]s this means substring matching; 550 * 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 551 * 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, 552 * means the collection has a matching element. In the case of collections,
522 * [expected] can itself be a matcher. 553 * [expected] can itself be a matcher.
523 */ 554 */
524 Matcher contains(expected) => new _Contains(expected); 555 Matcher contains(expected) => new _Contains(expected);
525 556
526 class _Contains extends BaseMatcher { 557 class _Contains extends BaseMatcher {
527 558
528 final _expected; 559 final _expected;
529 560
530 const _Contains(this._expected); 561 const _Contains(this._expected);
531 562
532 bool matches(item) { 563 bool matches(item, MatchState matchState) {
533 if (item is String) { 564 if (item is String) {
534 return item.indexOf(_expected) >= 0; 565 return item.indexOf(_expected) >= 0;
535 } else if (item is Collection) { 566 } else if (item is Collection) {
536 if (_expected is Matcher) { 567 if (_expected is Matcher) {
537 return item.some((e) => _expected.matches(e)); 568 return item.some((e) => _expected.matches(e, matchState));
538 } else { 569 } else {
539 return item.some((e) => e == _expected); 570 return item.some((e) => e == _expected);
540 } 571 }
541 } else if (item is Map) { 572 } else if (item is Map) {
542 return item.containsKey(_expected); 573 return item.containsKey(_expected);
543 } 574 }
544 return false; 575 return false;
545 } 576 }
546 577
547 Description describe(Description description) => 578 Description describe(Description description) =>
548 description.add('contains ').addDescriptionOf(_expected); 579 description.add('contains ').addDescriptionOf(_expected);
549 } 580 }
550 581
551 /** 582 /**
552 * Returns a matcher that matches if the match argument is in 583 * Returns a matcher that matches if the match argument is in
553 * the expected value. This is the converse of [contains]. 584 * the expected value. This is the converse of [contains].
554 */ 585 */
555 Matcher isIn(expected) => new _In(expected); 586 Matcher isIn(expected) => new _In(expected);
556 587
557 class _In extends BaseMatcher { 588 class _In extends BaseMatcher {
558 589
559 final _expected; 590 final _expected;
560 591
561 const _In(this._expected); 592 const _In(this._expected);
562 593
563 bool matches(item) { 594 bool matches(item, MatchState matchState) {
564 if (_expected is String) { 595 if (_expected is String) {
565 return _expected.indexOf(item) >= 0; 596 return _expected.indexOf(item) >= 0;
566 } else if (_expected is Collection) { 597 } else if (_expected is Collection) {
567 return _expected.some((e) => e == item); 598 return _expected.some((e) => e == item);
568 } else if (_expected is Map) { 599 } else if (_expected is Map) {
569 return _expected.containsKey(item); 600 return _expected.containsKey(item);
570 } 601 }
571 return false; 602 return false;
572 } 603 }
573 604
574 Description describe(Description description) => 605 Description describe(Description description) =>
575 description.add('is in ').addDescriptionOf(_expected); 606 description.add('is in ').addDescriptionOf(_expected);
576 } 607 }
577 608
578 /** 609 /**
579 * Returns a matcher that uses an arbitrary function that returns 610 * Returns a matcher that uses an arbitrary function that returns
580 * true or false for the actual value. 611 * true or false for the actual value.
581 */ 612 */
582 Matcher predicate(f, [description = 'satisfies function']) => 613 Matcher predicate(f, [description = 'satisfies function']) =>
583 new _Predicate(f, description); 614 new _Predicate(f, description);
584 615
585 class _Predicate extends BaseMatcher { 616 class _Predicate extends BaseMatcher {
586 617
587 final _matcher; 618 final _matcher;
588 final String _description; 619 final String _description;
589 620
590 const _Predicate(this._matcher, this._description); 621 const _Predicate(this._matcher, this._description);
591 622
592 bool matches(item) => _matcher(item); 623 bool matches(item, MatchState matchState) => _matcher(item);
593 624
594 Description describe(Description description) => 625 Description describe(Description description) =>
595 description.add(_description); 626 description.add(_description);
596 } 627 }
OLDNEW
« no previous file with comments | « lib/unittest/collection_matchers.dart ('k') | lib/unittest/expect.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698