Index: pkg/matcher/lib/src/core_matchers.dart |
diff --git a/pkg/matcher/lib/src/core_matchers.dart b/pkg/matcher/lib/src/core_matchers.dart |
index 356476b24d0f33d49d373f6ef7bb4ad7a4f3cba2..cb76d6843de7ee97f4ba1df16964a777ec6c707b 100644 |
--- a/pkg/matcher/lib/src/core_matchers.dart |
+++ b/pkg/matcher/lib/src/core_matchers.dart |
@@ -85,7 +85,7 @@ class _IsSameAs extends Matcher { |
/// |
/// For [Iterable]s and [Map]s, this will recursively match the elements. To |
/// handle cyclic structures a recursion depth [limit] can be provided. The |
-/// default limit is 100. |
+/// default limit is 100. [Set]s will be compared order-independently. |
Matcher equals(expected, [int limit=100]) => |
expected is String |
? new _StringEqualsMatcher(expected) |
@@ -124,6 +124,26 @@ class _DeepMatcher extends Matcher { |
} |
} |
+ List _compareSets(Set expected, actual, matcher, depth, location) { |
+ if (actual is! Iterable) return ['is not Iterable', location]; |
+ actual = actual.toSet(); |
+ |
+ for (var expectedElement in expected) { |
+ if (actual.every((actualElement) => |
+ matcher(expectedElement, actualElement, location, depth) != null)) { |
+ return ['does not contain $expectedElement', location]; |
+ } |
+ } |
+ |
+ if (actual.length > expected.length) { |
+ return ['larger than expected', location]; |
+ } else if (actual.length < expected.length) { |
+ return ['smaller than expected', location]; |
+ } else { |
+ return null; |
+ } |
+ } |
+ |
List _recursiveMatch(expected, actual, String location, int depth) { |
// If the expected value is a matcher, try to match it. |
if (expected is Matcher) { |
@@ -146,37 +166,38 @@ class _DeepMatcher extends Matcher { |
if (depth > _limit) return ['recursion depth limit exceeded', location]; |
// If _limit is 1 we can only recurse one level into object. |
- bool canRecurse = depth == 0 || _limit > 1; |
- |
- if (expected is Iterable && canRecurse) { |
- return _compareIterables(expected, actual, _recursiveMatch, depth + 1, |
- location); |
- } |
- |
- if (expected is Map && canRecurse) { |
- if (actual is! Map) return ['expected a map', location]; |
+ if (depth == 0 || _limit > 1) { |
+ if (expected is Set) { |
+ return _compareSets(expected, actual, _recursiveMatch, depth + 1, |
+ location); |
+ } else if (expected is Iterable) { |
+ return _compareIterables(expected, actual, _recursiveMatch, depth + 1, |
+ location); |
+ } else if (expected is Map) { |
+ if (actual is! Map) return ['expected a map', location]; |
+ |
+ var err = (expected.length == actual.length) ? '' : |
+ 'has different length and '; |
+ for (var key in expected.keys) { |
+ if (!actual.containsKey(key)) { |
+ return ["${err}is missing map key '$key'", location]; |
+ } |
+ } |
- var err = (expected.length == actual.length) ? '' : |
- 'has different length and '; |
- for (var key in expected.keys) { |
- if (!actual.containsKey(key)) { |
- return ["${err}is missing map key '$key'", location]; |
+ for (var key in actual.keys) { |
+ if (!expected.containsKey(key)) { |
+ return ["${err}has extra map key '$key'", location]; |
+ } |
} |
- } |
- for (var key in actual.keys) { |
- if (!expected.containsKey(key)) { |
- return ["${err}has extra map key '$key'", location]; |
+ for (var key in expected.keys) { |
+ var rp = _recursiveMatch(expected[key], actual[key], |
+ "${location}['${key}']", depth + 1); |
+ if (rp != null) return rp; |
} |
- } |
- for (var key in expected.keys) { |
- var rp = _recursiveMatch(expected[key], actual[key], |
- "${location}['${key}']", depth + 1); |
- if (rp != null) return rp; |
+ return null; |
} |
- |
- return null; |
} |
var description = new StringDescription(); |