Index: packages/matcher/lib/src/core_matchers.dart |
diff --git a/packages/matcher/lib/src/core_matchers.dart b/packages/matcher/lib/src/core_matchers.dart |
index 94f9d7d4f3eaf09a47637da01b9bac92b24a2d30..e8fdb11dd14b485d1c7a3e1695cd94b74224730b 100644 |
--- a/packages/matcher/lib/src/core_matchers.dart |
+++ b/packages/matcher/lib/src/core_matchers.dart |
@@ -110,60 +110,70 @@ Matcher equals(expected, [int limit = 100]) => expected is String |
? new _StringEqualsMatcher(expected) |
: new _DeepMatcher(expected, limit); |
+typedef _RecursiveMatcher = List<String> Function( |
+ dynamic, dynamic, String, int); |
+ |
class _DeepMatcher extends Matcher { |
final _expected; |
final int _limit; |
- var count; |
_DeepMatcher(this._expected, [int limit = 1000]) : this._limit = limit; |
// Returns a pair (reason, location) |
- List _compareIterables(expected, actual, matcher, depth, location) { |
- if (actual is! Iterable) return ['is not Iterable', location]; |
- |
- var expectedIterator = expected.iterator; |
- var actualIterator = actual.iterator; |
- for (var index = 0;; index++) { |
- // Advance in lockstep. |
- var expectedNext = expectedIterator.moveNext(); |
- var actualNext = actualIterator.moveNext(); |
- |
- // If we reached the end of both, we succeeded. |
- if (!expectedNext && !actualNext) return null; |
- |
- // Fail if their lengths are different. |
- var newLocation = '${location}[${index}]'; |
- if (!expectedNext) return ['longer than expected', newLocation]; |
- if (!actualNext) return ['shorter than expected', newLocation]; |
- |
- // Match the elements. |
- var rp = matcher( |
- expectedIterator.current, actualIterator.current, newLocation, depth); |
- if (rp != null) return rp; |
+ List<String> _compareIterables(Iterable expected, Object actual, |
+ _RecursiveMatcher matcher, int depth, String location) { |
+ if (actual is Iterable) { |
+ var expectedIterator = expected.iterator; |
+ var actualIterator = actual.iterator; |
+ for (var index = 0;; index++) { |
+ // Advance in lockstep. |
+ var expectedNext = expectedIterator.moveNext(); |
+ var actualNext = actualIterator.moveNext(); |
+ |
+ // If we reached the end of both, we succeeded. |
+ if (!expectedNext && !actualNext) return null; |
+ |
+ // Fail if their lengths are different. |
+ var newLocation = '$location[$index]'; |
+ if (!expectedNext) return ['longer than expected', newLocation]; |
+ if (!actualNext) return ['shorter than expected', newLocation]; |
+ |
+ // Match the elements. |
+ var rp = matcher(expectedIterator.current, actualIterator.current, |
+ newLocation, depth); |
+ if (rp != null) return rp; |
+ } |
+ } else { |
+ return ['is not Iterable', location]; |
} |
} |
- List _compareSets(Set expected, actual, matcher, depth, location) { |
- if (actual is! Iterable) return ['is not Iterable', location]; |
- actual = actual.toSet(); |
+ List<String> _compareSets(Set expected, Object actual, |
+ _RecursiveMatcher matcher, int depth, String location) { |
+ if (actual is Iterable) { |
+ Set other = actual.toSet(); |
- for (var expectedElement in expected) { |
- if (actual.every((actualElement) => |
- matcher(expectedElement, actualElement, location, depth) != null)) { |
- return ['does not contain $expectedElement', location]; |
+ for (var expectedElement in expected) { |
+ if (other.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]; |
+ if (other.length > expected.length) { |
+ return ['larger than expected', location]; |
+ } else if (other.length < expected.length) { |
+ return ['smaller than expected', location]; |
+ } else { |
+ return null; |
+ } |
} else { |
- return null; |
+ return ['is not Iterable', location]; |
} |
} |
- List _recursiveMatch(expected, actual, String location, int depth) { |
+ List<String> _recursiveMatch( |
+ Object expected, Object actual, String location, int depth) { |
// If the expected value is a matcher, try to match it. |
if (expected is Matcher) { |
var matchState = {}; |
@@ -194,17 +204,16 @@ class _DeepMatcher extends Matcher { |
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 '; |
+ var map = (actual as Map); |
+ var err = |
+ (expected.length == map.length) ? '' : 'has different length and '; |
for (var key in expected.keys) { |
- if (!actual.containsKey(key)) { |
+ if (!map.containsKey(key)) { |
return ["${err}is missing map key '$key'", location]; |
} |
} |
- for (var key in actual.keys) { |
+ for (var key in map.keys) { |
if (!expected.containsKey(key)) { |
return ["${err}has extra map key '$key'", location]; |
} |
@@ -212,7 +221,7 @@ class _DeepMatcher extends Matcher { |
for (var key in expected.keys) { |
var rp = _recursiveMatch( |
- expected[key], actual[key], "${location}['${key}']", depth + 1); |
+ expected[key], map[key], "$location['$key']", depth + 1); |
if (rp != null) return rp; |
} |
@@ -240,7 +249,7 @@ class _DeepMatcher extends Matcher { |
String _match(expected, actual, Map matchState) { |
var rp = _recursiveMatch(expected, actual, '', 0); |
if (rp == null) return null; |
- var reason; |
+ String reason; |
if (rp[0].length > 0) { |
if (rp[1].length > 0) { |
reason = "${rp[0]} at location ${rp[1]}"; |
@@ -330,7 +339,7 @@ class _StringEqualsMatcher extends Matcher { |
buff.write('^\n Differ at offset $start'); |
} |
- return mismatchDescription.replace(buff.toString()); |
+ return mismatchDescription.add(buff.toString()); |
} |
} |
@@ -568,18 +577,19 @@ class _In extends Matcher { |
/// For example: |
/// |
/// expect(v, predicate((x) => ((x % 2) == 0), "is even")) |
-Matcher predicate(bool f(value), [String description = 'satisfies function']) => |
+Matcher predicate<T>(bool f(T value), |
+ [String description = 'satisfies function']) => |
new _Predicate(f, description); |
-typedef bool _PredicateFunction(value); |
+typedef bool _PredicateFunction<T>(T value); |
-class _Predicate extends Matcher { |
- final _PredicateFunction _matcher; |
+class _Predicate<T> extends Matcher { |
+ final _PredicateFunction<T> _matcher; |
final String _description; |
- const _Predicate(this._matcher, this._description); |
+ _Predicate(this._matcher, this._description); |
- bool matches(item, Map matchState) => _matcher(item); |
+ bool matches(item, Map matchState) => _matcher(item as T); |
Description describe(Description description) => |
description.add(_description); |
@@ -595,15 +605,18 @@ class _Predicate extends Matcher { |
/// have a Widget class where each Widget has a price; we could make a |
/// [CustomMatcher] that can make assertions about prices with: |
/// |
-/// class HasPrice extends CustomMatcher { |
-/// const HasPrice(matcher) : |
-/// super("Widget with price that is", "price", matcher); |
-/// featureValueOf(actual) => actual.price; |
-/// } |
+/// ```dart |
+/// class HasPrice extends CustomMatcher { |
+/// HasPrice(matcher) : super("Widget with price that is", "price", matcher); |
+/// featureValueOf(actual) => actual.price; |
+/// } |
+/// ``` |
/// |
/// and then use this for example like: |
/// |
-/// expect(inventoryItem, new HasPrice(greaterThan(0))); |
+/// ```dart |
+/// expect(inventoryItem, new HasPrice(greaterThan(0))); |
+/// ``` |
class CustomMatcher extends Matcher { |
final String _featureDescription; |
final String _featureName; |