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

Unified Diff: lib/unittest/core_matchers.dart

Issue 10441104: New expectation functions plus convert old tests to use these. (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
« no previous file with comments | « lib/unittest/collection_matchers.dart ('k') | lib/unittest/description.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/unittest/core_matchers.dart
===================================================================
--- lib/unittest/core_matchers.dart (revision 0)
+++ lib/unittest/core_matchers.dart (revision 0)
@@ -0,0 +1,410 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+
+/**
+ * Returns a matcher that matches empty strings, maps or collections.
+ */
+final Matcher isEmpty = const _Empty();
+
+class _Empty extends BaseMatcher {
+ const _Empty();
+ bool matches(item) {
+ if (item is Map || item is Collection) {
+ return item.isEmpty();
+ } else if (item is String) {
+ return item.length == 0;
+ } else {
+ return false;
+ }
+ }
+ Description describe(Description description) =>
+ description.add('empty');
+}
+
+/** A matcher that matches any null value. */
+final Matcher isNull = const _IsNull();
+
+/** A matcher that matches any non-null value. */
+final Matcher isNotNull = const _IsNotNull();
+
+class _IsNull extends BaseMatcher {
+ const _IsNull();
+ bool matches(item) => item == null;
+ Description describe(Description description) =>
+ description.add('null');
+}
+
+class _IsNotNull extends BaseMatcher {
+ const _IsNotNull();
+ bool matches(item) => item != null;
+ Description describe(Description description) =>
+ description.add('not null');
+}
+
+/** A matcher that matches the Boolean value true. */
+final Matcher isTrue = const _IsTrue();
+
+/** A matcher that matches anything except the Boolean value true. */
+final Matcher isFalse = const _IsFalse();
+
+class _IsTrue extends BaseMatcher {
+ const _IsTrue();
+ bool matches(item) => item == true;
+ Description describe(Description description) =>
+ description.add('true');
+}
+
+class _IsFalse extends BaseMatcher {
+ const _IsFalse();
+ bool matches(item) => item != true;
+ Description describe(Description description) =>
+ description.add('false');
+}
+
+/**
+ * Returns a matches that matches if the value is the same instance
+ * as [object] (`===`).
+ */
+Matcher same(expected) => new _IsSameAs(expected);
+
+class _IsSameAs extends BaseMatcher {
+ final _expected;
+ const _IsSameAs(this._expected);
+ bool matches(item) => item === _expected;
+ // If all types were hashable we could show a hash here.
+ Description describe(Description description) =>
+ description.add('same instance as ').addDescriptionOf(_expected);
+}
+
+/** Returns a matcher that matches if two objects are equal (==). */
+Matcher equals(expected) => new _IsEqual(expected);
+
+class _IsEqual extends BaseMatcher {
+ final _expected;
+ const _IsEqual(this._expected);
+ bool matches(item) => item == _expected;
+ Description describe(Description description) =>
+ description.addDescriptionOf(_expected);
+}
+
+/** A matcher that matches any value. */
+final Matcher anything = const _IsAnything();
+
+class _IsAnything extends BaseMatcher {
+ const _IsAnything();
+ bool matches(item) => true;
+ Description describe(Description description) =>
+ description.add('anything');
+}
+
+/**
+ * Returns a matcher that matches if an object is an instance
+ * of [type] (or a subtype).
+ *
+ * As types are not first class objects in Dart we can only
+ * approximate this test by using a generic wrapper class.
+ *
+ * For example, to test whether 'bar' is an instance of type
+ * 'Foo', we would write:
+ *
+ * expect(bar, new isInstanceOf<Foo>());
+ *
+ * To get better error message, supply a name when creating the
+ * Type wrapper; e.g.:
+ *
+ * expect(bar, new isInstanceOf<Foo>('Foo'));
+ */
+class isInstanceOf<T> extends BaseMatcher {
+ final String _name;
+ const isInstanceOf([this._name = 'specified type']);
+ bool matches(obj) => obj is T;
+ // The description here is lame :-(
+ Description describe(Description description) =>
+ description.add('an instance of ${_name}');
+}
+
+/**
+ * A matcher that matches functions that throw exceptions when called.
+ * The value passed to expect() should be a reference to the function.
+ * Note that the function cannot take arguments; to handle this
+ * a wrapper will have to be created.
+ * The function will be called once upon success, or twice upon failure
+ * (the second time to get the failure description).
+ */
+final Matcher throws = const _Throws();
+
+/**
+ * Returns a matcher that matches a function call against an exception,
+ * which is in turn constrained by a [matcher].
+ * The value passed to expect() should be a reference to the function.
+ * Note that the function cannot take arguments; to handle this
+ * a wrapper will have to be created.
+ * The function will be called once upon success, or twice upon failure
+ * (the second time to get the failure description).
+ */
+Matcher throwsA(Matcher matcher) => new _Throws(matcher);
+
+/**
+ * A matcher that matches a function call against no exception.
+ * The function will be called once. Any exceptions will be silently swallowed.
+ * The value passed to expect() should be a reference to the function.
+ * Note that the function cannot take arguments; to handle this
+ * a wrapper will have to be created.
+ */
+final Matcher returnsNormally = const _ReturnsNormally();
+
+class _Throws extends BaseMatcher {
+ final Matcher _matcher;
+
+ const _Throws([Matcher this._matcher = null]);
+
+ bool matches(item) {
+ try {
+ item();
+ return false;
+ } catch (final e) {
+ return _matcher == null || _matcher.matches(e);
+ }
+ }
+
+ Description describe(Description description) {
+ if (_matcher == null) {
+ return description.add("throws an exception");
+ } else {
+ return description.add('throws an exception which matches ').
+ addDescriptionOf(_matcher);
+ }
+ }
+
+ Description describeMismatch(item, Description mismatchDescription) {
+ try {
+ item();
+ return mismatchDescription.add(' no exception');
+ } catch (final e) {
+ return mismatchDescription.add(' exception does not match ').
+ addDescriptionOf(_matcher);
+ }
+ }
+}
+
+class _ReturnsNormally extends BaseMatcher {
+
+ const _ReturnsNormally();
+
+ bool matches(f) {
+ try {
+ f();
+ return true;
+ } catch (final e) {
+ return false;
+ }
+ }
+
+ Description describe(Description description) =>
+ description.add("return normally");
+
+ Description describeMismatch(item, Description mismatchDescription) {
+ return mismatchDescription.add(' threw exception');
+ }
+}
+
+/** A matcher for functions that throw BadNumberFormatException */
+final Matcher throwsBadNumberFormatException =
+ new _Throws(new isInstanceOf<BadNumberFormatException>());
+
+/** A matcher for functions that throw an Exception */
+final Matcher throwsException =
+ new _Throws(new isInstanceOf<Exception>());
+
+/** A matcher for functions that throw an IllegalArgumentException */
+final Matcher throwsIllegalArgumentException =
+ new _Throws(new isInstanceOf<IllegalArgumentException>());
+
+/** A matcher for functions that throw an IllegalJSRegExpException */
+final Matcher throwsIllegalJSRegExpException =
+ new _Throws(new isInstanceOf<IllegalJSRegExpException>());
+
+/** A matcher for functions that throw an IndexOutOfRangeException */
+final Matcher throwsIndexOutOfRangeException =
+ new _Throws(new isInstanceOf<IndexOutOfRangeException>());
+
+/** A matcher for functions that throw a NoSuchMethodException */
+final Matcher throwsNoSuchMethodException =
+ new _Throws(new isInstanceOf<NoSuchMethodException>());
+
+/** A matcher for functions that throw a NotImplementedException */
+final Matcher throwsNotImplementedException =
+ new _Throws(new isInstanceOf<NotImplementedException>());
+
+/** A matcher for functions that throw a NullPointerException */
+final Matcher throwsNullPointerException =
+ new _Throws(new isInstanceOf<NullPointerException>());
+
+/** A matcher for functions that throw an UnsupportedOperationException */
+final Matcher throwsUnsupportedOperationException =
+ new _Throws(new isInstanceOf<UnsupportedOperationException>());
+
+/**
+ * Returns a matcher that matches if an object has a length property
+ * that matches [matcher].
+ */
+Matcher hasLength(matcher) =>
+ new _HasLength(wrapMatcher(matcher));
+
+class _HasLength extends BaseMatcher {
+ final Matcher _matcher;
+ const _HasLength([Matcher this._matcher = null]);
+
+ bool matches(item) {
+ return _matcher.matches(item.length);
+ }
+
+ Description describe(Description description) =>
+ description.add('an object with length of ').
+ addDescriptionOf(_matcher);
+
+ Description describeMismatch(item, Description mismatchDescription) {
+ super.describeMismatch(item, mismatchDescription);
+ try {
+ // We want to generate a different description if there is no length
+ // property. This is harmless code that will throw if no length property
+ // but subtle enough that an optimizer shouldn't strip it out.
+ if (item.length * item.length >= 0) {
+ return mismatchDescription.add(' with length of ').
+ addDescriptionOf(item.length);
+ }
+ } catch (var e) {
+ return mismatchDescription.add(' has no length property');
+ }
+ }
+}
+
+/**
+ * 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, [this._limit = 1000]);
+
+ 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
+ * means the collection has a matching element. In the case of collections,
+ * [expected] can itself be a matcher.
+ */
+Matcher contains(expected) => new _Contains(expected);
+
+class _Contains extends BaseMatcher {
+
+ final _expected;
+
+ const _Contains(this._expected);
+
+ bool matches(item) {
+ if (item is String) {
+ return item.indexOf(_expected) >= 0;
+ } else if (item is Collection) {
+ if (_expected is Matcher) {
+ return item.some((e) => _expected.matches(e));
+ } else {
+ return item.some((e) => e == _expected);
+ }
+ } else if (item is Map) {
+ return item.containsKey(_expected);
+ }
+ return false;
+ }
+
+ Description describe(Description description) =>
+ description.add('contains ').addDescriptionOf(_expected);
+}
« 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