| 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();
|
|
|