Index: lib/unittest/collection_matchers.dart |
=================================================================== |
--- lib/unittest/collection_matchers.dart (revision 0) |
+++ lib/unittest/collection_matchers.dart (revision 0) |
@@ -0,0 +1,157 @@ |
+// 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 which matches [Collection]s in which all elements |
+ * match the given [matcher]. |
+ */ |
+Matcher everyElement(matcher) => new _EveryElement(wrapMatcher(matcher)); |
+ |
+class _EveryElement extends _CollectionMatcher { |
+ Matcher _matcher; |
+ |
+ _EveryElement(Matcher this._matcher); |
+ |
+ bool matches(item) { |
+ return item.every((e) => _matcher.matches(e)); |
+ } |
+ |
+ Description describe(Description description) => |
+ description.add('every element ').addDescriptionOf(_matcher); |
+} |
+ |
+/** |
+ * Returns a matcher which matches [Collection]s in which at least one |
+ * element matches the given [matcher]. |
+ */ |
+Matcher someElement(matcher) => new _SomeElement(wrapMatcher(matcher)); |
+ |
+class _SomeElement extends _CollectionMatcher { |
+ Matcher _matcher; |
+ |
+ _SomeElement(this._matcher); |
+ |
+ bool matches(item) { |
+ return item.some( (e) => _matcher.matches(e) ); |
+ } |
+ |
+ Description describe(Description description) => |
+ description.add('some element ').addDescriptionOf(_matcher); |
+} |
+ |
+/** |
+ * Returns a matcher which matches [Iterable]s that have the same |
+ * length and the same elements as [expected], and in the same order. |
+ */ |
+Matcher orderedEquals(Iterable expected) => new _OrderedEquals(expected); |
+ |
+class _OrderedEquals extends BaseMatcher { |
+ Iterable _expected; |
+ |
+ _OrderedEquals(this._expected); |
+ |
+ String _test(item) { |
+ return _compareIterables(_expected, item, |
+ (expected, actual, location) => expected == actual? null : location); |
+ } |
+ |
+ bool matches(item) => (_test(item) == null); |
+ |
+ Description describe(Description description) => |
+ description.add('equals ').addDescriptionOf(_expected).add(' ordered'); |
+ |
+ Description describeMismatch(item, Description mismatchDescription) => |
+ mismatchDescription.add(_test(item)); |
+} |
+ |
+/** |
+ * Returns a matcher which matches [Iterable]s that have the same |
+ * length and the same elements as [expected], but not necessarily in |
+ * the same order. Note that this is O(n^2) so should only be used on |
+ * small objects. |
+ */ |
+Matcher unorderedEquals(Iterable expected) => |
+ new _UnorderedEquals(expected); |
+ |
+class _UnorderedEquals extends BaseMatcher { |
+ Iterable _expected; |
+ |
+ _UnorderedEquals(Iterable this._expected); |
+ |
+ String _test(item) { |
+ if (item is !Iterable) { |
+ return 'not iterable'; |
+ } |
+ // Check the lengths are the same. |
+ var expectedLength = 0; |
+ if (_expected is Collection) { |
+ Collection cast = _expected; // "_expected as Collection" |
+ expectedLength = cast.length; |
+ } else { |
+ for (var element in _expected) { |
+ ++expectedLength; |
+ } |
+ } |
+ var actualLength = 0; |
+ if (item is Collection) { |
+ actualLength = item.length; |
+ } else { |
+ for (var element in item) { |
+ ++actualLength; |
+ } |
+ } |
+ if (expectedLength > actualLength) { |
+ return 'has too few elements (${actualLength} < ${expectedLength})'; |
+ } else if (expectedLength < actualLength) { |
+ return 'has too many elements (${actualLength} > ${expectedLength})'; |
+ } |
+ List<bool> matched = new List<bool>(actualLength); |
+ var expectedPosition = 0; |
+ for (var expectedElement in _expected) { |
+ var actualPosition = 0; |
+ var gotMatch = false; |
+ for (var actualElement in item) { |
+ if (!matched[actualPosition]) { |
+ if (expectedElement == actualElement) { |
+ matched[actualPosition] = gotMatch = true; |
+ break; |
+ } |
+ } |
+ ++actualPosition; |
+ } |
+ if (!gotMatch) { |
+ return 'has no match for element ${expectedElement} ' |
+ 'at position ${expectedPosition}'; |
+ } |
+ ++expectedPosition; |
+ } |
+ return null; |
+ } |
+ |
+ bool matches(item) => (_test(item) == null); |
+ |
+ Description describe(Description description) => |
+ description.add('equals ').addDescriptionOf(_expected).add(' unordered'); |
+ |
+ Description describeMismatch(item, Description mismatchDescription) => |
+ mismatchDescription.add(_test(item)); |
+} |
+ |
+/** |
+ * Collection matchers match against [Collection]s. We add this intermediate |
+ * class to give better mismatch error messages than the base Matcher class. |
+ */ |
+ |
+/*abstract*/ class _CollectionMatcher extends BaseMatcher { |
+ const _CollectionMatcher(); |
+ Description describeMismatch(item, Description mismatchDescription) { |
+ if (item is !Collection) { |
+ return mismatchDescription. |
+ addDescriptionOf(item). |
+ add(' not a collection'); |
+ } else { |
+ return super.describeMismatch(item, mismatchDescription); |
+ } |
+ } |
+} |