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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: lib/unittest/core_matchers.dart
===================================================================
--- lib/unittest/core_matchers.dart (revision 8828)
+++ lib/unittest/core_matchers.dart (working copy)
@@ -78,15 +78,114 @@
description.add('same instance as ').addDescriptionOf(_expected);
}
-/** Returns a matcher that matches if two objects are equal (==). */
-Matcher equals(expected) => new _IsEqual(expected);
+/**
+ * Returns a matcher that does a deep recursive match. This only works
+ * with scalars, Maps and Iterables. To handle cyclic structures a
+ * recursion depth [limit can be pr]ovided. The default limit is 100.
Bob Nystrom 2012/06/19 23:38:33 That ] is in a weird place.
gram 2012/06/20 17:44:14 Telekinesis
+ */
+Matcher equals(expected, [limit=100]) =>
+ new _DeepMatcher(expected, limit);
-class _IsEqual extends BaseMatcher {
+class _DeepMatcher extends BaseMatcher {
final _expected;
- const _IsEqual(this._expected);
- bool matches(item) => item == _expected;
+ final int _limit;
+ var count;
+
+ _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit;
+
+ // A utility function for comparing iterators
Bob Nystrom 2012/06/19 23:38:33 This comment doesn't add a lot of value.
gram 2012/06/20 17:44:14 Removed
+
+ String _compareIterables(expected, actual, matcher, depth) {
+ if (actual is !Iterable) {
+ return 'is not Iterable';
+ }
+ var expectedIterator = expected.iterator();
+ var actualIterator = actual.iterator();
+ var position = 0;
+ String reason = null;
+ while (reason == null) {
+ if (expectedIterator.hasNext()) {
+ if (actualIterator.hasNext()) {
+ reason = matcher(expectedIterator.next(),
+ actualIterator.next(),
+ 'mismatch at position ${position}',
+ depth);
+ ++position;
+ } else {
+ reason = 'shorter than expected';
+ }
+ } else if (actualIterator.hasNext()) {
+ reason = 'longer than expected';
+ } else {
+ return null;
+ }
+ }
+ return reason;
+ }
+
+ Description _recursiveMatch(expected, actual, String location, int depth) {
+ Description reason = null;
+ // If _limit is 1 we can only recurse one level into object
Bob Nystrom 2012/06/19 23:38:33 Add a "."
gram 2012/06/20 17:44:14 Done.
+ bool canRecurse = depth == 0 || _limit > 1;
+ if (expected != actual) {
Bob Nystrom 2012/06/19 23:38:33 This is some painfully nested flow control. Can yo
gram 2012/06/20 17:44:14 Done.
+ if (depth > _limit) {
+ reason = new StringDescription('recursion depth limit exceeded');
Bob Nystrom 2012/06/19 23:38:33 Extra space before "new".
gram 2012/06/20 17:44:14 Done.
+ } else {
+ if (expected is Iterable && canRecurse) {
+ String r = _compareIterables(expected, actual,
+ _recursiveMatch, depth+1);
+ if (r != null) reason = new StringDescription(r);
+ } else if (expected is Map && canRecurse) {
+ if (actual is !Map) {
+ reason = new StringDescription('expected a map');
+ } else if (expected.length != actual.length) {
+ reason = new StringDescription('different map lengths');
+ } else {
+ for (var key in expected.getKeys()) {
+ if (!actual.containsKey(key)) {
+ reason = new StringDescription('missing map key ');
+ reason.addDescriptionOf(key);
+ break;
+ }
+ reason = _recursiveMatch(expected[key], actual[key],
+ 'with key <${key}> ${location}', depth+1);
+ if (reason != null) {
+ break;
+ }
+ }
+ }
+ } else {
+ // If we have recursed, show the expected value too; if not,
+ // expect() will show it for us.
+ reason = new StringDescription();
+ if (depth > 1) {
+ reason.add('expected ').addDescriptionOf(expected).add(' but was ').
+ addDescriptionOf(actual);
+ } else {
+ reason.add('was ').addDescriptionOf(actual);
+ }
+ }
+ }
+ }
+ if (reason == null) {
+ return null;
+ } else {
+ return reason.add(' ').add(location);
+ }
+ }
+
+ String _match(expected, actual) {
+ Description reason = _recursiveMatch(expected, actual, '', 0);
+ return reason == null ? null : reason.toString();
+ }
+
+ bool matches(item) => _match(_expected, item) == null;
+
Description describe(Description description) =>
- description.addDescriptionOf(_expected);
+ description.addDescriptionOf(_expected);
+
+ Description describeMismatch(item, Description mismatchDescription) =>
+ mismatchDescription.add(_match(_expected, item));
}
/** A matcher that matches any value. */
@@ -240,42 +339,142 @@
}
}
+/*
+ * Matchers for different exception types. Ideally we should just be able to
+ * use something like:
+ *
+ * final Matcher throwsException =
+ * const _Throws(const isInstanceOf<Exception>());
+ *
+ * Unfortunately instanceOf is not working with dart2js.
+ *
+ * Alternatively, if static functions could be used in const expressions,
+ * we could use:
+ *
+ * bool _isException(x) => x is Exception;
+ * final Matcher isException = const _Predicate(_isException, "Exception");
+ * final Matcher throwsException = const _Throws(isException);
+ *
+ * But currently using static functions in const expressions is not supported.
+ * For now the only solution for all platforms seems to be separate classes
+ * for each exception type.
+ */
+
+/* abstract */ class _ExceptionMatcher extends BaseMatcher {
+ final String _name;
+ const _ExceptionMatcher(this._name);
+ Description describe(Description description) =>
+ description.add(_name);
+}
+
+/** A matcher for BadNumberFormatExceptions. */
+final isBadNumberFormatException = const _BadNumberFormatException();
+
/** A matcher for functions that throw BadNumberFormatException */
final Matcher throwsBadNumberFormatException =
- const _Throws(const isInstanceOf<BadNumberFormatException>());
+ const _Throws(isBadNumberFormatException);
-/** A matcher for functions that throw an Exception */
-final Matcher throwsException =
- const _Throws(const isInstanceOf<Exception>());
+class _BadNumberFormatException extends _ExceptionMatcher {
+ const _BadNumberFormatException() : super("BadNumberFormatException");
+ bool matches(item) => item is BadNumberFormatException;
+}
-/** A matcher for functions that throw an IllegalArgumentException */
+/** A matcher for Exceptions. */
+final isException = const _Exception();
+
+/** A matcher for functions that throw Exception */
+final Matcher throwsException = const _Throws(isException);
+
+class _Exception extends _ExceptionMatcher {
+ const _Exception() : super("Exception");
+ bool matches(item) => item is Exception;
+}
+
+/** A matcher for IllegalArgumentExceptions. */
+final isIllegalArgumentException = const _IllegalArgumentException();
+
+/** A matcher for functions that throw IllegalArgumentException */
final Matcher throwsIllegalArgumentException =
- const _Throws(const isInstanceOf<IllegalArgumentException>());
+ const _Throws(isIllegalArgumentException);
-/** A matcher for functions that throw an IllegalJSRegExpException */
+class _IllegalArgumentException extends _ExceptionMatcher {
+ const _IllegalArgumentException() : super("IllegalArgumentException");
+ bool matches(item) => item is IllegalArgumentException;
+}
+
+/** A matcher for IllegalJSRegExpExceptions. */
+final isIllegalJSRegExpException = const _IllegalJSRegExpException();
+
+/** A matcher for functions that throw IllegalJSRegExpException */
final Matcher throwsIllegalJSRegExpException =
- const _Throws(const isInstanceOf<IllegalJSRegExpException>());
+ const _Throws(isIllegalJSRegExpException);
-/** A matcher for functions that throw an IndexOutOfRangeException */
+class _IllegalJSRegExpException extends _ExceptionMatcher {
+ const _IllegalJSRegExpException() : super("IllegalJSRegExpException");
+ bool matches(item) => item is IllegalJSRegExpException;
+}
+
+/** A matcher for IndexOutOfRangeExceptions. */
+final isIndexOutOfRangeException = const _IndexOutOfRangeException();
+
+/** A matcher for functions that throw IndexOutOfRangeException */
final Matcher throwsIndexOutOfRangeException =
- const _Throws(const isInstanceOf<IndexOutOfRangeException>());
+ const _Throws(isIndexOutOfRangeException);
-/** A matcher for functions that throw a NoSuchMethodException */
+class _IndexOutOfRangeException extends _ExceptionMatcher {
+ const _IndexOutOfRangeException() : super("IndexOutOfRangeException");
+ bool matches(item) => item is IndexOutOfRangeException;
+}
+
+/** A matcher for NoSuchMethodExceptions. */
+final isNoSuchMethodException = const _NoSuchMethodException();
+
+/** A matcher for functions that throw NoSuchMethodException */
final Matcher throwsNoSuchMethodException =
- const _Throws(const isInstanceOf<NoSuchMethodException>());
+ const _Throws(isNoSuchMethodException);
-/** A matcher for functions that throw a NotImplementedException */
+class _NoSuchMethodException extends _ExceptionMatcher {
+ const _NoSuchMethodException() : super("NoSuchMethodException");
+ bool matches(item) => item is NoSuchMethodException;
+}
+
+/** A matcher for NotImplementedExceptions. */
+final isNotImplementedException = const _NotImplementedException();
+
+/** A matcher for functions that throw Exception */
final Matcher throwsNotImplementedException =
- const _Throws(const isInstanceOf<NotImplementedException>());
+ const _Throws(isNotImplementedException);
-/** A matcher for functions that throw a NullPointerException */
+class _NotImplementedException extends _ExceptionMatcher {
+ const _NotImplementedException() : super("NotImplementedException");
+ bool matches(item) => item is NotImplementedException;
+}
+
+/** A matcher for NullPointerExceptions. */
+final isNullPointerException = const _NullPointerException();
+
+/** A matcher for functions that throw NotNullPointerException */
final Matcher throwsNullPointerException =
- const _Throws(const isInstanceOf<NullPointerException>());
+ const _Throws(isNullPointerException);
-/** A matcher for functions that throw an UnsupportedOperationException */
+class _NullPointerException extends _ExceptionMatcher {
+ const _NullPointerException() : super("NullPointerException");
+ bool matches(item) => item is NullPointerException;
+}
+
+/** A matcher for UnsupportedOperationExceptions. */
+final isUnsupportedOperationException = const _UnsupportedOperationException();
+
+/** A matcher for functions that throw UnsupportedOperationException */
final Matcher throwsUnsupportedOperationException =
- const _Throws(const isInstanceOf<UnsupportedOperationException>());
+ const _Throws(isUnsupportedOperationException);
+class _UnsupportedOperationException extends _ExceptionMatcher {
+ const _UnsupportedOperationException() :
+ super("UnsupportedOperationException");
+ bool matches(item) => item is UnsupportedOperationException;
+}
+
/**
* Returns a matcher that matches if an object has a length property
* that matches [matcher].
@@ -312,100 +511,6 @@
}
/**
- * Returns a matcher that does a deep recursive match. This only works
- * with scalars, Maps and Iterables. To handle cyclic structures an
- * item [limit] can be provided; if after [limit] items have been
- * compared and the process is not complete this will be treated as
- * a mismatch. The default limit is 1000.
- */
-Matcher recursivelyMatches(expected, [limit=1000]) =>
- new _DeepMatcher(expected, limit);
-
-// A utility function for comparing iterators
-
-String _compareIterables(expected, actual, matcher) {
- if (actual is !Iterable) {
- return 'is not Iterable';
- }
- var expectedIterator = expected.iterator();
- var actualIterator = actual.iterator();
- var position = 0;
- String reason = null;
- while (reason == null) {
- if (expectedIterator.hasNext()) {
- if (actualIterator.hasNext()) {
- reason = matcher(expectedIterator.next(),
- actualIterator.next(),
- 'mismatch at position ${position}');
- ++position;
- } else {
- reason = 'shorter than expected';
- }
- } else if (actualIterator.hasNext()) {
- reason = 'longer than expected';
- } else {
- return null;
- }
- }
- return reason;
-}
-
-class _DeepMatcher extends BaseMatcher {
- final _expected;
- final int _limit;
- var count;
-
- _DeepMatcher(this._expected, [limit = 1000]) : this._limit = limit;
-
- String _recursiveMatch(expected, actual, String location) {
- String reason = null;
- if (++count >= _limit) {
- reason = 'item comparison limit exceeded';
- } else if (expected is Iterable) {
- reason = _compareIterables(expected, actual, _recursiveMatch);
- } else if (expected is Map) {
- if (actual is !Map) {
- reason = 'expected a map';
- } else if (expected.length != actual.length) {
- reason = 'different map lengths';
- } else {
- for (var key in expected.getKeys()) {
- if (!actual.containsKey(key)) {
- reason = 'missing map key ${key}';
- break;
- }
- reason = _recursiveMatch(expected[key], actual[key],
- 'with key ${key} ${location}');
- if (reason != null) {
- break;
- }
- }
- }
- } else if (expected != actual) {
- reason = 'expected ${expected} but got ${actual}';
- }
- if (reason == null) {
- return null;
- } else {
- return '${reason} ${location}';
- }
- }
-
- String _match(expected, actual) {
- count = 0;
- return _recursiveMatch(expected, actual, '');
- }
-
- bool matches(item) => _match(_expected, item) == null;
-
- Description describe(Description description) =>
- description.add('recursively matches ').addDescriptionOf(_expected);
-
- Description describeMismatch(item, Description mismatchDescription) =>
- mismatchDescription.add(_match(_expected, item));
-}
-
-/**
* Returns a matcher that matches if the match argument contains
* the expected value. For [String]s this means substring matching;
* for [Map]s is means the map has the key, and for [Collection]s it
@@ -439,3 +544,49 @@
description.add('contains ').addDescriptionOf(_expected);
}
+/**
+ * Returns a matcher that matches if the match argument is in
+ * the expected value. This is the converse of [contains].
+ */
+Matcher isIn(expected) => new _In(expected);
+
Sean Eagan 2012/06/20 18:00:22 Thanks for adding this! Just a small gripe, I thi
+class _In extends BaseMatcher {
+
+ final _expected;
+
+ const _In(this._expected);
+
+ bool matches(item) {
+ if (_expected is String) {
+ return _expected.indexOf(item) >= 0;
+ } else if (_expected is Collection) {
+ return _expected.some((e) => e == item);
+ } else if (_expected is Map) {
+ return _expected.containsKey(item);
+ }
+ return false;
+ }
+
+ Description describe(Description description) =>
+ description.add('is in ').addDescriptionOf(_expected);
+}
+
+/**
+ * Returns a matcher that uses an arbitrary function that returns
+ * true or false for the actual value.
+ */
+Matcher predicate(f, [description = 'satisfies function']) =>
+ new _Predicate(f, description);
+
+class _Predicate extends BaseMatcher {
+
+ final _matcher;
+ final String _description;
+
+ const _Predicate(this._matcher, this._description);
+
+ bool matches(item) => _matcher(item);
+
+ Description describe(Description description) =>
+ description.add(_description);
+}

Powered by Google App Engine
This is Rietveld 408576698