pkg/matcher: refactored code around data-structure vs execution-based matchers

Also fixed a spelling error.

R=sigmund@google.com

Review URL: https://codereview.chromium.org//438473002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/matcher@38807 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c18134d..8e7bd6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.11.1+1
+
+* Refactored libraries and tests.
+
+* Fixed spelling mistake.
+
 ## 0.11.1
 
 * Added `isNaN` and `isNotNaN` matchers.
diff --git a/lib/matcher.dart b/lib/matcher.dart
index 99fe44b..6fd18cc 100644
--- a/lib/matcher.dart
+++ b/lib/matcher.dart
@@ -16,3 +16,6 @@
 export 'src/numeric_matchers.dart';
 export 'src/operator_matchers.dart';
 export 'src/string_matchers.dart';
+export 'src/throws_matcher.dart';
+export 'src/throws_matchers.dart';
+export 'src/util.dart';
diff --git a/lib/src/core_matchers.dart b/lib/src/core_matchers.dart
index f1a724a..8de3a01 100644
--- a/lib/src/core_matchers.dart
+++ b/lib/src/core_matchers.dart
@@ -4,11 +4,9 @@
 
 library matcher.core_matchers;
 
-import 'dart:async';
-
 import 'description.dart';
-import 'expect.dart';
 import 'interfaces.dart';
+import 'util.dart';
 
 /// Returns a matcher that matches empty strings, maps or iterables
 /// (including collections).
@@ -390,36 +388,6 @@
       description.add('an instance of ${_name}');
 }
 
-/// This can be used to match two kinds of objects:
-///
-///   * A [Function] that throws an exception when called. The function cannot
-///     take any arguments. If you want to test that a function expecting
-///     arguments throws, wrap it in another zero-argument function that calls
-///     the one you want to test.
-///
-///   * A [Future] that completes with an exception. Note that this creates an
-///     asynchronous expectation. The call to `expect()` that includes this will
-///     return immediately and execution will continue. Later, when the future
-///     completes, the actual expectation will run.
-const Matcher throws = const Throws();
-
-/// This can be used to match two kinds of objects:
-///
-///   * A [Function] that throws an exception when called. The function cannot
-///     take any arguments. If you want to test that a function expecting
-///     arguments throws, wrap it in another zero-argument function that calls
-///     the one you want to test.
-///
-///   * A [Future] that completes with an exception. Note that this creates an
-///     asynchronous expectation. The call to `expect()` that includes this will
-///     return immediately and execution will continue. Later, when the future
-///     completes, the actual expectation will run.
-///
-/// In both cases, when an exception is thrown, this will test that the exception
-/// object matches [matcher]. If [matcher] is not an instance of [Matcher], it
-/// will implicitly be treated as `equals(matcher)`.
-Matcher throwsA(matcher) => new Throws(wrapMatcher(matcher));
-
 /// A matcher that matches a function call against no exception.
 /// The function will be called once. Any exceptions will be silently swallowed.
 /// The value passed to expect() should be a reference to the function.
@@ -427,77 +395,6 @@
 /// a wrapper will have to be created.
 const Matcher returnsNormally = const _ReturnsNormally();
 
-class Throws extends Matcher {
-  final Matcher _matcher;
-
-  const Throws([Matcher matcher]) : this._matcher = matcher;
-
-  bool matches(item, Map matchState) {
-    if (item is! Function && item is! Future) return false;
-    if (item is Future) {
-      var done = wrapAsync((fn) => fn());
-
-      // Queue up an asynchronous expectation that validates when the future
-      // completes.
-      item.then((value) {
-        done(() {
-          fail("Expected future to fail, but succeeded with '$value'.");
-        });
-      }, onError: (error, trace) {
-        done(() {
-          if (_matcher == null) return;
-          var reason;
-          if (trace != null) {
-            var stackTrace = trace.toString();
-            stackTrace = "  ${stackTrace.replaceAll("\n", "\n  ")}";
-            reason = "Actual exception trace:\n$stackTrace";
-          }
-          expect(error, _matcher, reason: reason);
-        });
-      });
-      // It hasn't failed yet.
-      return true;
-    }
-
-    try {
-      item();
-      return false;
-    } catch (e, s) {
-      if (_matcher == null || _matcher.matches(e, matchState)) {
-        return true;
-      } else {
-        addStateInfo(matchState, {'exception': e, 'stack': s});
-        return false;
-      }
-    }
-  }
-
-  Description describe(Description description) {
-    if (_matcher == null) {
-      return description.add("throws");
-    } else {
-      return description.add('throws ').addDescriptionOf(_matcher);
-    }
-  }
-
-  Description describeMismatch(item, Description mismatchDescription,
-                               Map matchState,
-                               bool verbose) {
-    if (item is! Function && item is! Future) {
-      return mismatchDescription.add('is not a Function or Future');
-    } else if (_matcher == null || matchState['exception'] == null) {
-      return mismatchDescription.add('did not throw');
-    } else {
-      mismatchDescription. add('threw ').
-          addDescriptionOf(matchState['exception']);
-      if (verbose) {
-        mismatchDescription.add(' at ').add(matchState['stack'].toString());
-      }
-      return mismatchDescription;
-    }
-  }
-}
-
 class _ReturnsNormally extends Matcher {
   const _ReturnsNormally();
 
diff --git a/lib/src/error_matchers.dart b/lib/src/error_matchers.dart
index dca1759..fd0445c 100644
--- a/lib/src/error_matchers.dart
+++ b/lib/src/error_matchers.dart
@@ -10,9 +10,6 @@
 /// A matcher for ArgumentErrors.
 const Matcher isArgumentError = const _ArgumentError();
 
-/// A matcher for functions that throw ArgumentError.
-const Matcher throwsArgumentError = const Throws(isArgumentError);
-
 class _ArgumentError extends TypeMatcher {
   const _ArgumentError(): super("ArgumentError");
   bool matches(item, Map matchState) => item is ArgumentError;
@@ -22,10 +19,6 @@
 const Matcher isConcurrentModificationError =
     const _ConcurrentModificationError();
 
-/// A matcher for functions that throw ConcurrentModificationError.
-const Matcher throwsConcurrentModificationError =
-  const Throws(isConcurrentModificationError);
-
 class _ConcurrentModificationError extends TypeMatcher {
   const _ConcurrentModificationError(): super("ConcurrentModificationError");
   bool matches(item, Map matchState) => item is ConcurrentModificationError;
@@ -34,10 +27,6 @@
 /// A matcher for CyclicInitializationError.
 const Matcher isCyclicInitializationError = const _CyclicInitializationError();
 
-/// A matcher for functions that throw CyclicInitializationError.
-const Matcher throwsCyclicInitializationError =
-  const Throws(isCyclicInitializationError);
-
 class _CyclicInitializationError extends TypeMatcher {
   const _CyclicInitializationError(): super("CyclicInitializationError");
   bool matches(item, Map matchState) => item is CyclicInitializationError;
@@ -46,9 +35,6 @@
 /// A matcher for Exceptions.
 const Matcher isException = const _Exception();
 
-/// A matcher for functions that throw Exception.
-const Matcher throwsException = const Throws(isException);
-
 class _Exception extends TypeMatcher {
   const _Exception(): super("Exception");
   bool matches(item, Map matchState) => item is Exception;
@@ -62,9 +48,6 @@
 /// A matcher for FormatExceptions.
 const Matcher isFormatException = const _FormatException();
 
-/// A matcher for functions that throw FormatException.
-const Matcher throwsFormatException = const Throws(isFormatException);
-
 class _FormatException extends TypeMatcher {
   const _FormatException(): super("FormatException");
   bool matches(item, Map matchState) => item is FormatException;
@@ -73,9 +56,6 @@
 /// A matcher for NoSuchMethodErrors.
 const Matcher isNoSuchMethodError = const _NoSuchMethodError();
 
-/// A matcher for functions that throw NoSuchMethodError.
-const Matcher throwsNoSuchMethodError = const Throws(isNoSuchMethodError);
-
 class _NoSuchMethodError extends TypeMatcher {
   const _NoSuchMethodError(): super("NoSuchMethodError");
   bool matches(item, Map matchState) => item is NoSuchMethodError;
@@ -84,9 +64,6 @@
 /// A matcher for NullThrownError.
 const Matcher isNullThrownError = const _NullThrownError();
 
-/// A matcher for functions that throw NullThrownError.
-const Matcher throwsNullThrownError = const Throws(isNullThrownError);
-
 class _NullThrownError extends TypeMatcher {
   const _NullThrownError(): super("NullThrownError");
   bool matches(item, Map matchState) => item is NullThrownError;
@@ -95,9 +72,6 @@
 /// A matcher for RangeErrors.
 const Matcher isRangeError = const _RangeError();
 
-/// A matcher for functions that throw RangeError.
-const Matcher throwsRangeError = const Throws(isRangeError);
-
 class _RangeError extends TypeMatcher {
   const _RangeError(): super("RangeError");
   bool matches(item, Map matchState) => item is RangeError;
@@ -106,9 +80,6 @@
 /// A matcher for StateErrors.
 const Matcher isStateError = const _StateError();
 
-/// A matcher for functions that throw StateError.
-const Matcher throwsStateError = const Throws(isStateError);
-
 class _StateError extends TypeMatcher {
   const _StateError(): super("StateError");
   bool matches(item, Map matchState) => item is StateError;
@@ -117,9 +88,6 @@
 /// A matcher for UnimplementedErrors.
 const Matcher isUnimplementedError = const _UnimplementedError();
 
-/// A matcher for functions that throw Exception.
-const Matcher throwsUnimplementedError = const Throws(isUnimplementedError);
-
 class _UnimplementedError extends TypeMatcher {
   const _UnimplementedError(): super("UnimplementedError");
   bool matches(item, Map matchState) => item is UnimplementedError;
@@ -128,9 +96,6 @@
 /// A matcher for UnsupportedError.
 const Matcher isUnsupportedError = const _UnsupportedError();
 
-/// A matcher for functions that throw UnsupportedError.
-const Matcher throwsUnsupportedError = const Throws(isUnsupportedError);
-
 class _UnsupportedError extends TypeMatcher {
   const _UnsupportedError(): super("UnsupportedError");
   bool matches(item, Map matchState) => item is UnsupportedError;
diff --git a/lib/src/expect.dart b/lib/src/expect.dart
index 073ef34..bd54747 100644
--- a/lib/src/expect.dart
+++ b/lib/src/expect.dart
@@ -7,6 +7,7 @@
 import 'core_matchers.dart';
 import 'description.dart';
 import 'interfaces.dart';
+import 'util.dart';
 
 /// The objects thrown by the default failure handler.
 class TestFailure extends Error {
@@ -17,15 +18,33 @@
   String toString() => message;
 }
 
-/// Useful utility for nesting match states.
+/// Failed matches are reported using a default IFailureHandler.
+/// The default implementation simply throws [TestFailure]s;
+/// this can be replaced by some other implementation of
+/// IFailureHandler by calling configureExpectHandler.
+abstract class FailureHandler {
+  /// This handles failures given a textual decription
+  void fail(String reason);
 
-void addStateInfo(Map matchState, Map values) {
-  var innerState = new Map.from(matchState);
-  matchState.clear();
-  matchState['state'] = innerState;
-  matchState.addAll(values);
+  /// This handles failures given the actual [value], the [matcher]
+  /// the [reason] (argument from [expect]), some additonal [matchState]
+  /// generated by the [matcher], and a verbose flag which controls in
+  /// some cases how much [matchState] information is used. It will use
+  /// these to create a detailed error message (typically by calling
+  /// an [ErrorFormatter]) and then call [fail] with this message.
+  void failMatch(actual, Matcher matcher, String reason,
+                 Map matchState, bool verbose);
 }
 
+/// The ErrorFormatter type is used for functions that
+/// can be used to build up error reports upon [expect] failures.
+/// There is one built-in implementation ([defaultErrorFormatter])
+/// which is used by the default failure handler. If the failure handler
+/// is replaced it may be desirable to replace the [stringDescription]
+/// error formatter with another.
+typedef String ErrorFormatter(actual, Matcher matcher, String reason,
+  Map matchState, bool verbose);
+
 /// Some matchers, like those for Futures and exception testing,
 /// can fail in asynchronous sections, and throw exceptions.
 /// A user of this library will typically want to catch and handle
@@ -81,20 +100,6 @@
   failureHandler.fail(message);
 }
 
-/// Takes an argument and returns an equivalent matcher.
-/// If the argument is already a matcher this does nothing,
-/// else if the argument is a function, it generates a predicate
-/// function matcher, else it generates an equals matcher.
-Matcher wrapMatcher(x) {
-  if (x is Matcher) {
-    return x;
-  } else if (x is Function) {
-    return predicate(x);
-  } else {
-    return equals(x);
-  }
-}
-
 // The handler for failed asserts.
 FailureHandler _assertFailureHandler = null;
 
diff --git a/lib/src/future_matchers.dart b/lib/src/future_matchers.dart
index 4c5a391..f1d7cb8 100644
--- a/lib/src/future_matchers.dart
+++ b/lib/src/future_matchers.dart
@@ -6,9 +6,9 @@
 
 import 'dart:async';
 
-import 'core_matchers.dart';
 import 'expect.dart';
 import 'interfaces.dart';
+import 'util.dart';
 
 /// Matches a [Future] that completes successfully with a value. Note that this
 /// creates an asynchronous expectation. The call to `expect()` that includes
diff --git a/lib/src/interfaces.dart b/lib/src/interfaces.dart
index 9fc1107..f3f0755 100644
--- a/lib/src/interfaces.dart
+++ b/lib/src/interfaces.dart
@@ -7,15 +7,6 @@
 // To decouple the reporting of errors, and allow for extensibility of
 // matchers, we make use of some interfaces.
 
-/// The ErrorFormatter type is used for functions that
-/// can be used to build up error reports upon [expect] failures.
-/// There is one built-in implementation ([defaultErrorFormatter])
-/// which is used by the default failure handler. If the failure handler
-/// is replaced it may be desirable to replace the [stringDescription]
-/// error formatter with another.
-typedef String ErrorFormatter(actual, Matcher matcher, String reason,
-    Map matchState, bool verbose);
-
 /// Matchers build up their error messages by appending to
 /// Description objects. This interface is implemented by
 /// StringDescription. This interface is unlikely to need
@@ -58,7 +49,7 @@
   /// This builds a textual description of a specific mismatch. [item]
   /// is the value that was tested by [matches]; [matchState] is
   /// the [Map] that was passed to and supplemented by [matches]
-  /// with additional information about the mismact, and [mismatchDescription]
+  /// with additional information about the mismatch, and [mismatchDescription]
   /// is the [Description] that is being built to decribe the mismatch.
   /// A few matchers make use of the [verbose] flag to provide detailed
   /// information that is not typically included but can be of help in
@@ -66,21 +57,3 @@
   Description describeMismatch(item, Description mismatchDescription,
       Map matchState, bool verbose) => mismatchDescription;
 }
-
-/// Failed matches are reported using a default IFailureHandler.
-/// The default implementation simply throws [TestFailure]s;
-/// this can be replaced by some other implementation of
-/// IFailureHandler by calling configureExpectHandler.
-abstract class FailureHandler {
-  /// This handles failures given a textual decription
-  void fail(String reason);
-
-  /// This handles failures given the actual [value], the [matcher]
-  /// the [reason] (argument from [expect]), some additonal [matchState]
-  /// generated by the [matcher], and a verbose flag which controls in
-  /// some cases how much [matchState] information is used. It will use
-  /// these to create a detailed error message (typically by calling
-  /// an [ErrorFormatter]) and then call [fail] with this message.
-  void failMatch(actual, Matcher matcher, String reason,
-                 Map matchState, bool verbose);
-}
diff --git a/lib/src/iterable_matchers.dart b/lib/src/iterable_matchers.dart
index ef7afb0..eeea0ee 100644
--- a/lib/src/iterable_matchers.dart
+++ b/lib/src/iterable_matchers.dart
@@ -6,8 +6,8 @@
 
 import 'core_matchers.dart';
 import 'description.dart';
-import 'expect.dart';
 import 'interfaces.dart';
+import 'util.dart';
 
 /// Returns a matcher which matches [Iterable]s in which all elements
 /// match the given [matcher].
diff --git a/lib/src/map_matchers.dart b/lib/src/map_matchers.dart
index a560e50..623b380 100644
--- a/lib/src/map_matchers.dart
+++ b/lib/src/map_matchers.dart
@@ -4,8 +4,8 @@
 
 library matcher.map_matchers;
 
-import 'expect.dart';
 import 'interfaces.dart';
+import 'util.dart';
 
 /// Returns a matcher which matches maps containing the given [value].
 Matcher containsValue(value) => new _ContainsValue(value);
diff --git a/lib/src/operator_matchers.dart b/lib/src/operator_matchers.dart
index fb79e8f..a6d23c2 100644
--- a/lib/src/operator_matchers.dart
+++ b/lib/src/operator_matchers.dart
@@ -4,9 +4,8 @@
 
 library matcher.operator_matchers;
 
-import 'core_matchers.dart';
-import 'expect.dart';
 import 'interfaces.dart';
+import 'util.dart';
 
 /// This returns a matcher that inverts [matcher] to its logical negation.
 Matcher isNot(matcher) => new _IsNot(wrapMatcher(matcher));
@@ -14,7 +13,7 @@
 class _IsNot extends Matcher {
   final Matcher _matcher;
 
-  const _IsNot(Matcher this._matcher);
+  const _IsNot(this._matcher);
 
   bool matches(item, Map matchState) => !_matcher.matches(item, matchState);
 
@@ -103,40 +102,21 @@
 }
 
 List<Matcher> _wrapArgs(arg0, arg1, arg2, arg3, arg4, arg5, arg6) {
+  Iterable<Matcher> matchers;
   if (arg0 is List) {
-    // TODO(kevmoo) throw a more helpful error here if any of these args is
-    // not null
-    expect(arg1, isNull);
-    expect(arg2, isNull);
-    expect(arg3, isNull);
-    expect(arg4, isNull);
-    expect(arg5, isNull);
-    expect(arg6, isNull);
+    if (arg1 != null || arg2 != null || arg3 != null || arg4 != null ||
+        arg5 != null || arg6 != null) {
+      throw new ArgumentError('If arg0 is a List, all other arguments must be'
+          ' null.');
+    }
 
-    return arg0.map((a) => wrapMatcher(a)).toList();
+    matchers = arg0;
+  } else {
+    matchers = [arg0, arg1, arg2, arg3, arg4, arg5, arg6]
+        .where((e) => e != null);
   }
 
-  List matchers = new List();
-  if (arg0 != null) {
-    matchers.add(wrapMatcher(arg0));
-  }
-  if (arg1 != null) {
-    matchers.add(wrapMatcher(arg1));
-  }
-  if (arg2 != null) {
-    matchers.add(wrapMatcher(arg2));
-  }
-  if (arg3 != null) {
-    matchers.add(wrapMatcher(arg3));
-  }
-  if (arg4 != null) {
-    matchers.add(wrapMatcher(arg4));
-  }
-  if (arg5 != null) {
-    matchers.add(wrapMatcher(arg5));
-  }
-  if (arg6 != null) {
-    matchers.add(wrapMatcher(arg6));
-  }
-  return matchers;
+  return matchers
+      .map((e) => wrapMatcher(e))
+      .toList();
 }
diff --git a/lib/src/throws_matcher.dart b/lib/src/throws_matcher.dart
new file mode 100644
index 0000000..b3a5323
--- /dev/null
+++ b/lib/src/throws_matcher.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2014, 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.
+
+library matcher.throws_matcher;
+
+import 'dart:async';
+
+import 'expect.dart';
+import 'interfaces.dart';
+import 'util.dart';
+
+/// This can be used to match two kinds of objects:
+///
+///   * A [Function] that throws an exception when called. The function cannot
+///     take any arguments. If you want to test that a function expecting
+///     arguments throws, wrap it in another zero-argument function that calls
+///     the one you want to test.
+///
+///   * A [Future] that completes with an exception. Note that this creates an
+///     asynchronous expectation. The call to `expect()` that includes this will
+///     return immediately and execution will continue. Later, when the future
+///     completes, the actual expectation will run.
+const Matcher throws = const Throws();
+
+/// This can be used to match two kinds of objects:
+///
+///   * A [Function] that throws an exception when called. The function cannot
+///     take any arguments. If you want to test that a function expecting
+///     arguments throws, wrap it in another zero-argument function that calls
+///     the one you want to test.
+///
+///   * A [Future] that completes with an exception. Note that this creates an
+///     asynchronous expectation. The call to `expect()` that includes this will
+///     return immediately and execution will continue. Later, when the future
+///     completes, the actual expectation will run.
+///
+/// In both cases, when an exception is thrown, this will test that the exception
+/// object matches [matcher]. If [matcher] is not an instance of [Matcher], it
+/// will implicitly be treated as `equals(matcher)`.
+Matcher throwsA(matcher) => new Throws(wrapMatcher(matcher));
+
+class Throws extends Matcher {
+  final Matcher _matcher;
+
+  const Throws([Matcher matcher]) : this._matcher = matcher;
+
+  bool matches(item, Map matchState) {
+    if (item is! Function && item is! Future) return false;
+    if (item is Future) {
+      var done = wrapAsync((fn) => fn());
+
+      // Queue up an asynchronous expectation that validates when the future
+      // completes.
+      item.then((value) {
+        done(() {
+          fail("Expected future to fail, but succeeded with '$value'.");
+        });
+      }, onError: (error, trace) {
+        done(() {
+          if (_matcher == null) return;
+          var reason;
+          if (trace != null) {
+            var stackTrace = trace.toString();
+            stackTrace = "  ${stackTrace.replaceAll("\n", "\n  ")}";
+            reason = "Actual exception trace:\n$stackTrace";
+          }
+          expect(error, _matcher, reason: reason);
+        });
+      });
+      // It hasn't failed yet.
+      return true;
+    }
+
+    try {
+      item();
+      return false;
+    } catch (e, s) {
+      if (_matcher == null || _matcher.matches(e, matchState)) {
+        return true;
+      } else {
+        addStateInfo(matchState, {'exception': e, 'stack': s});
+        return false;
+      }
+    }
+  }
+
+  Description describe(Description description) {
+    if (_matcher == null) {
+      return description.add("throws");
+    } else {
+      return description.add('throws ').addDescriptionOf(_matcher);
+    }
+  }
+
+  Description describeMismatch(item, Description mismatchDescription,
+                               Map matchState,
+                               bool verbose) {
+    if (item is! Function && item is! Future) {
+      return mismatchDescription.add('is not a Function or Future');
+    } else if (_matcher == null || matchState['exception'] == null) {
+      return mismatchDescription.add('did not throw');
+    } else {
+      mismatchDescription. add('threw ').
+          addDescriptionOf(matchState['exception']);
+      if (verbose) {
+        mismatchDescription.add(' at ').add(matchState['stack'].toString());
+      }
+      return mismatchDescription;
+    }
+  }
+}
+
diff --git a/lib/src/throws_matchers.dart b/lib/src/throws_matchers.dart
new file mode 100644
index 0000000..35dfb39
--- /dev/null
+++ b/lib/src/throws_matchers.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2014, 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.
+
+library matcher.throws_matchers;
+
+import 'error_matchers.dart';
+import 'interfaces.dart';
+import 'throws_matcher.dart';
+
+/// A matcher for functions that throw ArgumentError.
+const Matcher throwsArgumentError = const Throws(isArgumentError);
+
+/// A matcher for functions that throw ConcurrentModificationError.
+const Matcher throwsConcurrentModificationError =
+    const Throws(isConcurrentModificationError);
+
+/// A matcher for functions that throw CyclicInitializationError.
+const Matcher throwsCyclicInitializationError =
+    const Throws(isCyclicInitializationError);
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsException = const Throws(isException);
+
+/// A matcher for functions that throw FormatException.
+const Matcher throwsFormatException = const Throws(isFormatException);
+
+/// A matcher for functions that throw NoSuchMethodError.
+const Matcher throwsNoSuchMethodError = const Throws(isNoSuchMethodError);
+
+/// A matcher for functions that throw NullThrownError.
+const Matcher throwsNullThrownError = const Throws(isNullThrownError);
+
+/// A matcher for functions that throw RangeError.
+const Matcher throwsRangeError = const Throws(isRangeError);
+
+/// A matcher for functions that throw StateError.
+const Matcher throwsStateError = const Throws(isStateError);
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsUnimplementedError = const Throws(isUnimplementedError);
+
+/// A matcher for functions that throw UnsupportedError.
+const Matcher throwsUnsupportedError = const Throws(isUnsupportedError);
diff --git a/lib/src/util.dart b/lib/src/util.dart
new file mode 100644
index 0000000..08cba77
--- /dev/null
+++ b/lib/src/util.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2014, 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.
+
+library matcher.util;
+
+import 'core_matchers.dart';
+import 'interfaces.dart';
+
+/// Useful utility for nesting match states.
+void addStateInfo(Map matchState, Map values) {
+  var innerState = new Map.from(matchState);
+  matchState.clear();
+  matchState['state'] = innerState;
+  matchState.addAll(values);
+}
+
+/// Takes an argument and returns an equivalent matcher.
+/// If the argument is already a matcher this does nothing,
+/// else if the argument is a function, it generates a predicate
+/// function matcher, else it generates an equals matcher.
+Matcher wrapMatcher(x) {
+  if (x is Matcher) {
+    return x;
+  } else if (x is Function) {
+    return predicate(x);
+  } else {
+    return equals(x);
+  }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 7cf3e9d..c6cb49d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: matcher
-version: 0.11.1
+version: 0.11.1+1
 author: Dart Team <misc@dartlang.org>
 description: Support for specifying test expectations
 homepage: https://pub.dartlang.org/packages/matcher
diff --git a/test/core_matchers_test.dart b/test/core_matchers_test.dart
index cfeddbe..99d10ac 100644
--- a/test/core_matchers_test.dart
+++ b/test/core_matchers_test.dart
@@ -83,38 +83,6 @@
     shouldFail(a, isNot(anything), "Expected: not anything Actual: {}");
   });
 
-  test('throws', () {
-    shouldFail(doesNotThrow, throws,
-        matches(
-            r"Expected: throws"
-            r"  Actual: <Closure(: \(\) => dynamic "
-            r"from Function 'doesNotThrow': static\.)?>"
-            r"   Which: did not throw"));
-    shouldPass(doesThrow, throws);
-    shouldFail(true, throws,
-        "Expected: throws"
-        "  Actual: <true>"
-        "   Which: is not a Function or Future");
-  });
-
-  test('throwsA', () {
-    shouldPass(doesThrow, throwsA(equals('X')));
-    shouldFail(doesThrow, throwsA(equals('Y')),
-        matches(
-            r"Expected: throws 'Y'"
-            r"  Actual: <Closure(: \(\) => dynamic "
-            r"from Function 'doesThrow': static\.)?>"
-            r"   Which: threw 'X'"));
-  });
-
-  test('throwsA', () {
-    shouldPass(doesThrow, throwsA(equals('X')));
-    shouldFail(doesThrow, throwsA(equals('Y')),
-        matches("Expected: throws 'Y'.*"
-        "Actual: <Closure.*"
-        "Which: threw 'X'"));
-  });
-
   test('returnsNormally', () {
     shouldPass(doesNotThrow, returnsNormally);
     shouldFail(doesThrow, returnsNormally,
@@ -209,31 +177,4 @@
       shouldPass('cow', predicate((x) => x is String, "an instance of String"));
     });
   });
-
-  group('exception/error matchers', () {
-    test('throwsCyclicInitializationError', () {
-      expect(() => _Bicycle.foo, throwsCyclicInitializationError);
-    });
-
-    test('throwsConcurrentModificationError', () {
-      expect(() {
-        var a = { 'foo': 'bar' };
-        for (var k in a.keys) {
-          a.remove(k);
-        }
-      }, throwsConcurrentModificationError);
-    });
-
-    test('throwsNullThrownError', () {
-      expect(() => throw null, throwsNullThrownError);
-    });
-  });
-}
-
-class _Bicycle {
-  static final foo = bar();
-
-  static bar() {
-    return foo + 1;
-  }
 }
diff --git a/test/operator_matchers_test.dart b/test/operator_matchers_test.dart
index fd9627a..f82abaa 100644
--- a/test/operator_matchers_test.dart
+++ b/test/operator_matchers_test.dart
@@ -13,16 +13,47 @@
   initUtils();
 
   test('anyOf', () {
+    // with a list
     shouldFail(0, anyOf([equals(1), equals(2)]),
         "Expected: (<1> or <2>) Actual: <0>");
     shouldPass(1, anyOf([equals(1), equals(2)]));
+
+    // with individual items
+    shouldFail(0, anyOf(equals(1), equals(2)),
+        "Expected: (<1> or <2>) Actual: <0>");
+    shouldPass(1, anyOf(equals(1), equals(2)));
   });
 
   test('allOf', () {
+    // with a list
     shouldPass(1, allOf([lessThan(10), greaterThan(0)]));
     shouldFail(-1, allOf([lessThan(10), greaterThan(0)]),
         "Expected: (a value less than <10> and a value greater than <0>) "
         "Actual: <-1> "
         "Which: is not a value greater than <0>");
+
+    // with individual items
+    shouldPass(1, allOf(lessThan(10), greaterThan(0)));
+    shouldFail(-1, allOf(lessThan(10), greaterThan(0)),
+        "Expected: (a value less than <10> and a value greater than <0>) "
+        "Actual: <-1> "
+        "Which: is not a value greater than <0>");
+
+    // with maximum items
+    shouldPass(1, allOf(lessThan(10), lessThan(9), lessThan(8), lessThan(7),
+                        lessThan(6), lessThan(5), lessThan(4)));
+    shouldFail(4, allOf(lessThan(10), lessThan(9), lessThan(8), lessThan(7),
+        lessThan(6), lessThan(5), lessThan(4)),
+        "Expected: (a value less than <10> and a value less than <9> and a "
+        "value less than <8> and a value less than <7> and a value less than "
+        "<6> and a value less than <5> and a value less than <4>) "
+        "Actual: <4> "
+        "Which: is not a value less than <4>");
+  });
+
+  test('If the first argument is a List, the rest must be null', () {
+    expect(() => allOf([], 5), throwsArgumentError);
+    expect(() => anyOf([], null, null, null, null, null, 42),
+        throwsArgumentError);
   });
 }
diff --git a/test/throws_matchers_test.dart b/test/throws_matchers_test.dart
new file mode 100644
index 0000000..74ff82d
--- /dev/null
+++ b/test/throws_matchers_test.dart
@@ -0,0 +1,76 @@
+// 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.
+
+library matcher.core_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+
+  test('throws', () {
+    shouldFail(doesNotThrow, throws,
+        matches(
+            r"Expected: throws"
+            r"  Actual: <Closure(: \(\) => dynamic "
+            r"from Function 'doesNotThrow': static\.)?>"
+            r"   Which: did not throw"));
+    shouldPass(doesThrow, throws);
+    shouldFail(true, throws,
+        "Expected: throws"
+        "  Actual: <true>"
+        "   Which: is not a Function or Future");
+  });
+
+  test('throwsA', () {
+    shouldPass(doesThrow, throwsA(equals('X')));
+    shouldFail(doesThrow, throwsA(equals('Y')),
+        matches(
+            r"Expected: throws 'Y'"
+            r"  Actual: <Closure(: \(\) => dynamic "
+            r"from Function 'doesThrow': static\.)?>"
+            r"   Which: threw 'X'"));
+  });
+
+  test('throwsA', () {
+    shouldPass(doesThrow, throwsA(equals('X')));
+    shouldFail(doesThrow, throwsA(equals('Y')),
+        matches("Expected: throws 'Y'.*"
+        "Actual: <Closure.*"
+        "Which: threw 'X'"));
+  });
+
+
+  group('exception/error matchers', () {
+    test('throwsCyclicInitializationError', () {
+      expect(() => _Bicycle.foo, throwsCyclicInitializationError);
+    });
+
+    test('throwsConcurrentModificationError', () {
+      expect(() {
+        var a = { 'foo': 'bar' };
+        for (var k in a.keys) {
+          a.remove(k);
+        }
+      }, throwsConcurrentModificationError);
+    });
+
+    test('throwsNullThrownError', () {
+      expect(() => throw null, throwsNullThrownError);
+    });
+  });
+}
+
+class _Bicycle {
+  static final foo = bar();
+
+  static bar() {
+    return foo + 1;
+  }
+}
+