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

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

Issue 10579008: Added test setup/teardown. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 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/description.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
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
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) => 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 /** Returns a matcher that matches if two objects are equal (==). */ 81 /**
82 Matcher equals(expected) => new _IsEqual(expected); 82 * Returns a matcher that does a deep recursive match. This only works
83 * with scalars, Maps and Iterables. To handle cyclic structures a
84 * recursion depth [limit] can be provided. The default limit is 100.
85 */
86 Matcher equals(expected, [limit=100]) =>
87 new _DeepMatcher(expected, limit);
83 88
84 class _IsEqual extends BaseMatcher { 89 class _DeepMatcher extends BaseMatcher {
85 final _expected; 90 final _expected;
86 const _IsEqual(this._expected); 91 final int _limit;
87 bool matches(item) => item == _expected; 92 var count;
93
94 _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit;
95
96 String _compareIterables(expected, actual, matcher, depth) {
97 if (actual is !Iterable) {
98 return 'is not Iterable';
99 }
100 var expectedIterator = expected.iterator();
101 var actualIterator = actual.iterator();
102 var position = 0;
103 String reason = null;
104 while (reason == null) {
105 if (expectedIterator.hasNext()) {
106 if (actualIterator.hasNext()) {
107 reason = matcher(expectedIterator.next(),
108 actualIterator.next(),
109 'mismatch at position ${position}',
110 depth);
111 ++position;
112 } else {
113 reason = 'shorter than expected';
114 }
115 } else if (actualIterator.hasNext()) {
116 reason = 'longer than expected';
117 } else {
118 return null;
119 }
120 }
121 return reason;
122 }
123
124 Description _recursiveMatch(expected, actual, String location, int depth) {
125 Description reason = null;
126 // If _limit is 1 we can only recurse one level into object.
127 bool canRecurse = depth == 0 || _limit > 1;
128 if (expected == actual) {
129 // Do nothing.
130 } else if (depth > _limit) {
131 reason = new StringDescription('recursion depth limit exceeded');
132 } else {
133 if (expected is Iterable && canRecurse) {
134 String r = _compareIterables(expected, actual,
135 _recursiveMatch, depth+1);
136 if (r != null) reason = new StringDescription(r);
137 } else if (expected is Map && canRecurse) {
138 if (actual is !Map) {
139 reason = new StringDescription('expected a map');
140 } else if (expected.length != actual.length) {
141 reason = new StringDescription('different map lengths');
142 } else {
143 for (var key in expected.getKeys()) {
144 if (!actual.containsKey(key)) {
145 reason = new StringDescription('missing map key ');
146 reason.addDescriptionOf(key);
147 break;
148 }
149 reason = _recursiveMatch(expected[key], actual[key],
150 'with key <${key}> ${location}', depth+1);
151 if (reason != null) {
152 break;
153 }
154 }
155 }
156 } else {
157 // If we have recursed, show the expected value too; if not,
158 // expect() will show it for us.
159 reason = new StringDescription();
160 if (depth > 1) {
161 reason.add('expected ').addDescriptionOf(expected).add(' but was ').
162 addDescriptionOf(actual);
163 } else {
164 reason.add('was ').addDescriptionOf(actual);
165 }
166 }
167 }
168 if (reason != null) {
169 reason.add(' ').add(location);
170 }
171 return reason;
172 }
173
174 String _match(expected, actual) {
175 Description reason = _recursiveMatch(expected, actual, '', 0);
176 return reason == null ? null : reason.toString();
177 }
178
179 bool matches(item) => _match(_expected, item) == null;
180
88 Description describe(Description description) => 181 Description describe(Description description) =>
89 description.addDescriptionOf(_expected); 182 description.addDescriptionOf(_expected);
183
184 Description describeMismatch(item, Description mismatchDescription) =>
185 mismatchDescription.add(_match(_expected, item));
90 } 186 }
91 187
92 /** A matcher that matches any value. */ 188 /** A matcher that matches any value. */
93 final Matcher anything = const _IsAnything(); 189 final Matcher anything = const _IsAnything();
94 190
95 class _IsAnything extends BaseMatcher { 191 class _IsAnything extends BaseMatcher {
96 const _IsAnything(); 192 const _IsAnything();
97 bool matches(item) => true; 193 bool matches(item) => true;
98 Description describe(Description description) => 194 Description describe(Description description) =>
99 description.add('anything'); 195 description.add('anything');
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 } 329 }
234 330
235 Description describe(Description description) => 331 Description describe(Description description) =>
236 description.add("return normally"); 332 description.add("return normally");
237 333
238 Description describeMismatch(item, Description mismatchDescription) { 334 Description describeMismatch(item, Description mismatchDescription) {
239 return mismatchDescription.add(' threw exception'); 335 return mismatchDescription.add(' threw exception');
240 } 336 }
241 } 337 }
242 338
339 /*
340 * Matchers for different exception types. Ideally we should just be able to
341 * use something like:
342 *
343 * final Matcher throwsException =
344 * const _Throws(const isInstanceOf<Exception>());
345 *
346 * Unfortunately instanceOf is not working with dart2js.
347 *
348 * Alternatively, if static functions could be used in const expressions,
349 * we could use:
350 *
351 * bool _isException(x) => x is Exception;
352 * final Matcher isException = const _Predicate(_isException, "Exception");
353 * final Matcher throwsException = const _Throws(isException);
354 *
355 * But currently using static functions in const expressions is not supported.
356 * For now the only solution for all platforms seems to be separate classes
357 * for each exception type.
358 */
359
360 /* abstract */ class _ExceptionMatcher extends BaseMatcher {
361 final String _name;
362 const _ExceptionMatcher(this._name);
363 Description describe(Description description) =>
364 description.add(_name);
365 }
366
367 /** A matcher for BadNumberFormatExceptions. */
368 final isBadNumberFormatException = const _BadNumberFormatException();
369
243 /** A matcher for functions that throw BadNumberFormatException */ 370 /** A matcher for functions that throw BadNumberFormatException */
244 final Matcher throwsBadNumberFormatException = 371 final Matcher throwsBadNumberFormatException =
245 const _Throws(const isInstanceOf<BadNumberFormatException>()); 372 const _Throws(isBadNumberFormatException);
246 373
247 /** A matcher for functions that throw an Exception */ 374 class _BadNumberFormatException extends _ExceptionMatcher {
248 final Matcher throwsException = 375 const _BadNumberFormatException() : super("BadNumberFormatException");
249 const _Throws(const isInstanceOf<Exception>()); 376 bool matches(item) => item is BadNumberFormatException;
377 }
250 378
251 /** A matcher for functions that throw an IllegalArgumentException */ 379 /** A matcher for Exceptions. */
380 final isException = const _Exception();
381
382 /** A matcher for functions that throw Exception */
383 final Matcher throwsException = const _Throws(isException);
384
385 class _Exception extends _ExceptionMatcher {
386 const _Exception() : super("Exception");
387 bool matches(item) => item is Exception;
388 }
389
390 /** A matcher for IllegalArgumentExceptions. */
391 final isIllegalArgumentException = const _IllegalArgumentException();
392
393 /** A matcher for functions that throw IllegalArgumentException */
252 final Matcher throwsIllegalArgumentException = 394 final Matcher throwsIllegalArgumentException =
253 const _Throws(const isInstanceOf<IllegalArgumentException>()); 395 const _Throws(isIllegalArgumentException);
254 396
255 /** A matcher for functions that throw an IllegalJSRegExpException */ 397 class _IllegalArgumentException extends _ExceptionMatcher {
398 const _IllegalArgumentException() : super("IllegalArgumentException");
399 bool matches(item) => item is IllegalArgumentException;
400 }
401
402 /** A matcher for IllegalJSRegExpExceptions. */
403 final isIllegalJSRegExpException = const _IllegalJSRegExpException();
404
405 /** A matcher for functions that throw IllegalJSRegExpException */
256 final Matcher throwsIllegalJSRegExpException = 406 final Matcher throwsIllegalJSRegExpException =
257 const _Throws(const isInstanceOf<IllegalJSRegExpException>()); 407 const _Throws(isIllegalJSRegExpException);
258 408
259 /** A matcher for functions that throw an IndexOutOfRangeException */ 409 class _IllegalJSRegExpException extends _ExceptionMatcher {
410 const _IllegalJSRegExpException() : super("IllegalJSRegExpException");
411 bool matches(item) => item is IllegalJSRegExpException;
412 }
413
414 /** A matcher for IndexOutOfRangeExceptions. */
415 final isIndexOutOfRangeException = const _IndexOutOfRangeException();
416
417 /** A matcher for functions that throw IndexOutOfRangeException */
260 final Matcher throwsIndexOutOfRangeException = 418 final Matcher throwsIndexOutOfRangeException =
261 const _Throws(const isInstanceOf<IndexOutOfRangeException>()); 419 const _Throws(isIndexOutOfRangeException);
262 420
263 /** A matcher for functions that throw a NoSuchMethodException */ 421 class _IndexOutOfRangeException extends _ExceptionMatcher {
422 const _IndexOutOfRangeException() : super("IndexOutOfRangeException");
423 bool matches(item) => item is IndexOutOfRangeException;
424 }
425
426 /** A matcher for NoSuchMethodExceptions. */
427 final isNoSuchMethodException = const _NoSuchMethodException();
428
429 /** A matcher for functions that throw NoSuchMethodException */
264 final Matcher throwsNoSuchMethodException = 430 final Matcher throwsNoSuchMethodException =
265 const _Throws(const isInstanceOf<NoSuchMethodException>()); 431 const _Throws(isNoSuchMethodException);
266 432
267 /** A matcher for functions that throw a NotImplementedException */ 433 class _NoSuchMethodException extends _ExceptionMatcher {
434 const _NoSuchMethodException() : super("NoSuchMethodException");
435 bool matches(item) => item is NoSuchMethodException;
436 }
437
438 /** A matcher for NotImplementedExceptions. */
439 final isNotImplementedException = const _NotImplementedException();
440
441 /** A matcher for functions that throw Exception */
268 final Matcher throwsNotImplementedException = 442 final Matcher throwsNotImplementedException =
269 const _Throws(const isInstanceOf<NotImplementedException>()); 443 const _Throws(isNotImplementedException);
270 444
271 /** A matcher for functions that throw a NullPointerException */ 445 class _NotImplementedException extends _ExceptionMatcher {
446 const _NotImplementedException() : super("NotImplementedException");
447 bool matches(item) => item is NotImplementedException;
448 }
449
450 /** A matcher for NullPointerExceptions. */
451 final isNullPointerException = const _NullPointerException();
452
453 /** A matcher for functions that throw NotNullPointerException */
272 final Matcher throwsNullPointerException = 454 final Matcher throwsNullPointerException =
273 const _Throws(const isInstanceOf<NullPointerException>()); 455 const _Throws(isNullPointerException);
274 456
275 /** A matcher for functions that throw an UnsupportedOperationException */ 457 class _NullPointerException extends _ExceptionMatcher {
458 const _NullPointerException() : super("NullPointerException");
459 bool matches(item) => item is NullPointerException;
460 }
461
462 /** A matcher for UnsupportedOperationExceptions. */
463 final isUnsupportedOperationException = const _UnsupportedOperationException();
464
465 /** A matcher for functions that throw UnsupportedOperationException */
276 final Matcher throwsUnsupportedOperationException = 466 final Matcher throwsUnsupportedOperationException =
277 const _Throws(const isInstanceOf<UnsupportedOperationException>()); 467 const _Throws(isUnsupportedOperationException);
468
469 class _UnsupportedOperationException extends _ExceptionMatcher {
470 const _UnsupportedOperationException() :
471 super("UnsupportedOperationException");
472 bool matches(item) => item is UnsupportedOperationException;
473 }
278 474
279 /** 475 /**
280 * Returns a matcher that matches if an object has a length property 476 * Returns a matcher that matches if an object has a length property
281 * that matches [matcher]. 477 * that matches [matcher].
282 */ 478 */
283 Matcher hasLength(matcher) => 479 Matcher hasLength(matcher) =>
284 new _HasLength(wrapMatcher(matcher)); 480 new _HasLength(wrapMatcher(matcher));
285 481
286 class _HasLength extends BaseMatcher { 482 class _HasLength extends BaseMatcher {
287 final Matcher _matcher; 483 final Matcher _matcher;
(...skipping 17 matching lines...) Expand all
305 return mismatchDescription.add(' with length of '). 501 return mismatchDescription.add(' with length of ').
306 addDescriptionOf(item.length); 502 addDescriptionOf(item.length);
307 } 503 }
308 } catch (var e) { 504 } catch (var e) {
309 return mismatchDescription.add(' has no length property'); 505 return mismatchDescription.add(' has no length property');
310 } 506 }
311 } 507 }
312 } 508 }
313 509
314 /** 510 /**
315 * Returns a matcher that does a deep recursive match. This only works
316 * with scalars, Maps and Iterables. To handle cyclic structures an
317 * item [limit] can be provided; if after [limit] items have been
318 * compared and the process is not complete this will be treated as
319 * a mismatch. The default limit is 1000.
320 */
321 Matcher recursivelyMatches(expected, [limit=1000]) =>
322 new _DeepMatcher(expected, limit);
323
324 // A utility function for comparing iterators
325
326 String _compareIterables(expected, actual, matcher) {
327 if (actual is !Iterable) {
328 return 'is not Iterable';
329 }
330 var expectedIterator = expected.iterator();
331 var actualIterator = actual.iterator();
332 var position = 0;
333 String reason = null;
334 while (reason == null) {
335 if (expectedIterator.hasNext()) {
336 if (actualIterator.hasNext()) {
337 reason = matcher(expectedIterator.next(),
338 actualIterator.next(),
339 'mismatch at position ${position}');
340 ++position;
341 } else {
342 reason = 'shorter than expected';
343 }
344 } else if (actualIterator.hasNext()) {
345 reason = 'longer than expected';
346 } else {
347 return null;
348 }
349 }
350 return reason;
351 }
352
353 class _DeepMatcher extends BaseMatcher {
354 final _expected;
355 final int _limit;
356 var count;
357
358 _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit;
359
360 String _recursiveMatch(expected, actual, String location) {
361 String reason = null;
362 if (++count >= _limit) {
363 reason = 'item comparison limit exceeded';
364 } else if (expected is Iterable) {
365 reason = _compareIterables(expected, actual, _recursiveMatch);
366 } else if (expected is Map) {
367 if (actual is !Map) {
368 reason = 'expected a map';
369 } else if (expected.length != actual.length) {
370 reason = 'different map lengths';
371 } else {
372 for (var key in expected.getKeys()) {
373 if (!actual.containsKey(key)) {
374 reason = 'missing map key ${key}';
375 break;
376 }
377 reason = _recursiveMatch(expected[key], actual[key],
378 'with key ${key} ${location}');
379 if (reason != null) {
380 break;
381 }
382 }
383 }
384 } else if (expected != actual) {
385 reason = 'expected ${expected} but got ${actual}';
386 }
387 if (reason == null) {
388 return null;
389 } else {
390 return '${reason} ${location}';
391 }
392 }
393
394 String _match(expected, actual) {
395 count = 0;
396 return _recursiveMatch(expected, actual, '');
397 }
398
399 bool matches(item) => _match(_expected, item) == null;
400
401 Description describe(Description description) =>
402 description.add('recursively matches ').addDescriptionOf(_expected);
403
404 Description describeMismatch(item, Description mismatchDescription) =>
405 mismatchDescription.add(_match(_expected, item));
406 }
407
408 /**
409 * Returns a matcher that matches if the match argument contains 511 * Returns a matcher that matches if the match argument contains
410 * the expected value. For [String]s this means substring matching; 512 * the expected value. For [String]s this means substring matching;
411 * for [Map]s is means the map has the key, and for [Collection]s it 513 * for [Map]s is means the map has the key, and for [Collection]s it
412 * means the collection has a matching element. In the case of collections, 514 * means the collection has a matching element. In the case of collections,
413 * [expected] can itself be a matcher. 515 * [expected] can itself be a matcher.
414 */ 516 */
415 Matcher contains(expected) => new _Contains(expected); 517 Matcher contains(expected) => new _Contains(expected);
416 518
417 class _Contains extends BaseMatcher { 519 class _Contains extends BaseMatcher {
418 520
(...skipping 13 matching lines...) Expand all
432 } else if (item is Map) { 534 } else if (item is Map) {
433 return item.containsKey(_expected); 535 return item.containsKey(_expected);
434 } 536 }
435 return false; 537 return false;
436 } 538 }
437 539
438 Description describe(Description description) => 540 Description describe(Description description) =>
439 description.add('contains ').addDescriptionOf(_expected); 541 description.add('contains ').addDescriptionOf(_expected);
440 } 542 }
441 543
544 /**
545 * Returns a matcher that matches if the match argument is in
546 * the expected value. This is the converse of [contains].
547 */
548 Matcher isIn(expected) => new _In(expected);
549
550 class _In extends BaseMatcher {
551
552 final _expected;
553
554 const _In(this._expected);
555
556 bool matches(item) {
557 if (_expected is String) {
558 return _expected.indexOf(item) >= 0;
559 } else if (_expected is Collection) {
560 return _expected.some((e) => e == item);
561 } else if (_expected is Map) {
562 return _expected.containsKey(item);
563 }
564 return false;
565 }
566
567 Description describe(Description description) =>
568 description.add('is in ').addDescriptionOf(_expected);
569 }
570
571 /**
572 * Returns a matcher that uses an arbitrary function that returns
573 * true or false for the actual value.
574 */
575 Matcher predicate(f, [description = 'satisfies function']) =>
576 new _Predicate(f, description);
577
578 class _Predicate extends BaseMatcher {
579
580 final _matcher;
581 final String _description;
582
583 const _Predicate(this._matcher, this._description);
584
585 bool matches(item) => _matcher(item);
586
587 Description describe(Description description) =>
588 description.add(_description);
589 }
OLDNEW
« no previous file with comments | « lib/unittest/collection_matchers.dart ('k') | lib/unittest/description.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698