Chromium Code Reviews| Index: pkg/unittest/lib/src/iterable_matchers.dart |
| diff --git a/pkg/unittest/lib/src/iterable_matchers.dart b/pkg/unittest/lib/src/iterable_matchers.dart |
| index ab7827f115cb3001203840abb9c397c608aa4da4..a81bfed347b341512ff204baae05ff080be9e826 100644 |
| --- a/pkg/unittest/lib/src/iterable_matchers.dart |
| +++ b/pkg/unittest/lib/src/iterable_matchers.dart |
| @@ -108,6 +108,7 @@ class _OrderedEquals extends Matcher { |
| } |
| } |
| } |
| + |
| /** |
| * Returns a matcher which matches [Iterable]s that have the same |
| * length and the same elements as [expected], but not necessarily in |
| @@ -193,6 +194,73 @@ abstract class _IterableMatcher extends Matcher { |
| } |
| /** |
| + * Returns a matcher which matches [Iterable]s whose elements match the matchers |
| + * in [expected], but not necessarily in the same order. |
| + * |
| + * Note that this is `O(n^2)` and so should only be used on small objects. |
| + */ |
| +Matcher unorderedMatches(Iterable expected) => |
| + new _UnorderedMatches(expected); |
| + |
| +class _UnorderedMatches extends Matcher { |
|
Siggi Cherem (dart-lang)
2013/12/17 00:32:03
seems like there is a lot of code in common here a
nweiz
2013/12/17 03:33:47
I intentionally avoided going too deep on modifyin
Siggi Cherem (dart-lang)
2013/12/17 18:48:45
I think it is worth the effort for the matchers li
|
| + final List<Matcher> _expected; |
| + |
| + _UnorderedMatches(Iterable expected) |
| + : _expected = expected.map(wrapMatcher).toList(); |
| + |
| + String _test(item) { |
| + if (item is !Iterable) return 'not iterable'; |
| + item = item.toList(); |
| + |
| + // Check the lengths are the same. |
| + if (_expected.length > item.length) { |
| + return 'has too few elements (${item.length} < ${_expected.length})'; |
| + } else if (_expected.length < item.length) { |
| + return 'has too many elements (${item.length} > ${_expected.length})'; |
| + } |
| + |
| + var matched = new List<bool>.filled(item.length, false); |
| + var expectedPosition = 0; |
| + for (var expectedMatcher in _expected) { |
| + var actualPosition = 0; |
| + var gotMatch = false; |
| + for (var actualElement in item) { |
| + if (!matched[actualPosition]) { |
| + if (expectedMatcher.matches(actualElement, {})) { |
| + matched[actualPosition] = gotMatch = true; |
| + break; |
| + } |
| + } |
| + ++actualPosition; |
| + } |
| + |
| + if (!gotMatch) { |
| + return new StringDescription() |
| + .add('has no match for ') |
| + .addDescriptionOf(expectedMatcher) |
| + .add(' at index ${expectedPosition}') |
| + .toString(); |
| + } |
| + |
| + ++expectedPosition; |
| + } |
| + return null; |
| + } |
| + |
| + bool matches(item, Map mismatchState) => _test(item) == null; |
| + |
| + Description describe(Description description) => |
| + description |
| + .add('matches ') |
| + .addAll('[', ', ', ']', _expected) |
| + .add(' unordered'); |
| + |
| + Description describeMismatch(item, Description mismatchDescription, |
| + Map matchState, bool verbose) => |
| + mismatchDescription.add(_test(item)); |
| +} |
| + |
| +/** |
| * A pairwise matcher for iterable. You can pass an arbitrary [comparator] |
| * function that takes an expected and actual argument which will be applied |
| * to each pair in order. [description] should be a meaningful name for |