← Back to team overview

testtools-dev team mailing list archive

[Merge] lp:~jml/testtools/split-matchers into lp:testtools

 

Jonathan Lange has proposed merging lp:~jml/testtools/split-matchers into lp:testtools.

Requested reviews:
  testtools committers (testtools-committers)

For more details, see:
https://code.launchpad.net/~jml/testtools/split-matchers/+merge/123427

The matchers module is getting too big for me to think about clearly.

Here, I've split it up into what I think are fairly logical groupings.
The names should be mostly clear, except probably for '_core' and '_basic'.

_core is the actual matchers system.  _basic is a bunch of matchers for 
very common Python concepts.

Thoughts welcome.

jml
-- 
https://code.launchpad.net/~jml/testtools/split-matchers/+merge/123427
Your team testtools developers is subscribed to branch lp:testtools.
=== modified file 'NEWS'
--- NEWS	2012-08-10 14:32:06 +0000
+++ NEWS	2012-09-08 17:30:27 +0000
@@ -6,6 +6,13 @@
 NEXT
 ~~~~
 
+Changes
+-------
+
+* The ``testtools.matchers`` package has been split up.  No change to the
+  public interface.  (Jonathan Lange)
+
+
 0.9.16
 ~~~~~~
 

=== modified file 'testtools/__init__.py'
--- testtools/__init__.py	2012-08-10 14:32:06 +0000
+++ testtools/__init__.py	2012-09-08 17:30:27 +0000
@@ -34,7 +34,7 @@
     try_import,
     try_imports,
     )
-from testtools.matchers import (
+from testtools.matchers._core import (
     Matcher,
     )
 # Shut up, pyflakes. We are importing for documentation, not for namespacing.

=== added directory 'testtools/matchers'
=== added file 'testtools/matchers/__init__.py'
--- testtools/matchers/__init__.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/__init__.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,105 @@
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
+
+"""All the matchers.
+
+Matchers, a way to express complex assertions outside the testcase.
+
+Inspired by 'hamcrest'.
+
+Matcher provides the abstract API that all matchers need to implement.
+
+Bundled matchers are listed in __all__: a list can be obtained by running
+$ python -c 'import testtools.matchers; print testtools.matchers.__all__'
+"""
+
+__all__ = [
+    'AfterPreprocessing',
+    'AllMatch',
+    'Annotate',
+    'Contains',
+    'ContainsAll',
+    'DirExists',
+    'DocTestMatches',
+    'EndsWith',
+    'Equals',
+    'FileContains',
+    'FileExists',
+    'GreaterThan',
+    'HasPermissions',
+    'Is',
+    'IsInstance',
+    'KeysEqual',
+    'LessThan',
+    'MatchesAll',
+    'MatchesAny',
+    'MatchesException',
+    'MatchesListwise',
+    'MatchesPredicate',
+    'MatchesRegex',
+    'MatchesSetwise',
+    'MatchesStructure',
+    'NotEquals',
+    'Not',
+    'PathExists',
+    'Raises',
+    'raises',
+    'SamePath',
+    'StartsWith',
+    'TarballContains',
+    ]
+
+from ._basic import (
+    Contains,
+    EndsWith,
+    Equals,
+    GreaterThan,
+    Is,
+    IsInstance,
+    LessThan,
+    MatchesRegex,
+    NotEquals,
+    StartsWith,
+    )
+from ._datastructures import (
+    ContainsAll,
+    MatchesListwise,
+    MatchesSetwise,
+    MatchesStructure,
+    )
+from ._dict import (
+    KeysEqual,
+    )
+from ._doctest import (
+    DocTestMatches,
+    )
+from ._exception import (
+    MatchesException,
+    Raises,
+    raises,
+    )
+from ._filesystem import (
+    DirExists,
+    FileContains,
+    FileExists,
+    HasPermissions,
+    PathExists,
+    SamePath,
+    TarballContains,
+    )
+from ._higherorder import (
+    AfterPreprocessing,
+    AllMatch,
+    Annotate,
+    MatchesAll,
+    MatchesAny,
+    MatchesPredicate,
+    Not,
+    )
+
+# XXX: These are not explicitly included in __all__.  It's unclear how much of
+# the public interface they really are.
+from ._core import (
+    Matcher,
+    Mismatch,
+    MismatchError,
+    )

=== added file 'testtools/matchers/_basic.py'
--- testtools/matchers/_basic.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/_basic.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,315 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+__all__ = [
+    'Contains',
+    'EndsWith',
+    'Equals',
+    'GreaterThan',
+    'Is',
+    'IsInstance',
+    'LessThan',
+    'MatchesRegex',
+    'NotEquals',
+    'StartsWith',
+    ]
+
+import operator
+from pprint import pformat
+import re
+
+from ..compat import (
+    _isbytes,
+    istext,
+    str_is_unicode,
+    text_repr,
+    )
+from ..helpers import list_subtract
+from ._core import (
+    Matcher,
+    Mismatch,
+    )
+from ._higherorder import PostfixedMismatch
+
+
+def _format(thing):
+    """
+    Blocks of text with newlines are formatted as triple-quote
+    strings. Everything else is pretty-printed.
+    """
+    if istext(thing) or _isbytes(thing):
+        return text_repr(thing)
+    return pformat(thing)
+
+
+class _BinaryComparison(object):
+    """Matcher that compares an object to another object."""
+
+    def __init__(self, expected):
+        self.expected = expected
+
+    def __str__(self):
+        return "%s(%r)" % (self.__class__.__name__, self.expected)
+
+    def match(self, other):
+        if self.comparator(other, self.expected):
+            return None
+        return _BinaryMismatch(self.expected, self.mismatch_string, other)
+
+    def comparator(self, expected, other):
+        raise NotImplementedError(self.comparator)
+
+
+class _BinaryMismatch(Mismatch):
+    """Two things did not match."""
+
+    def __init__(self, expected, mismatch_string, other):
+        self.expected = expected
+        self._mismatch_string = mismatch_string
+        self.other = other
+
+    def describe(self):
+        left = repr(self.expected)
+        right = repr(self.other)
+        if len(left) + len(right) > 70:
+            return "%s:\nreference = %s\nactual    = %s\n" % (
+                self._mismatch_string, _format(self.expected),
+                _format(self.other))
+        else:
+            return "%s %s %s" % (left, self._mismatch_string, right)
+
+
+class Equals(_BinaryComparison):
+    """Matches if the items are equal."""
+
+    comparator = operator.eq
+    mismatch_string = '!='
+
+
+class NotEquals(_BinaryComparison):
+    """Matches if the items are not equal.
+
+    In most cases, this is equivalent to ``Not(Equals(foo))``. The difference
+    only matters when testing ``__ne__`` implementations.
+    """
+
+    comparator = operator.ne
+    mismatch_string = '=='
+
+
+class Is(_BinaryComparison):
+    """Matches if the items are identical."""
+
+    comparator = operator.is_
+    mismatch_string = 'is not'
+
+
+class LessThan(_BinaryComparison):
+    """Matches if the item is less than the matchers reference object."""
+
+    comparator = operator.__lt__
+    mismatch_string = 'is not >'
+
+
+class GreaterThan(_BinaryComparison):
+    """Matches if the item is greater than the matchers reference object."""
+
+    comparator = operator.__gt__
+    mismatch_string = 'is not <'
+
+
+class SameMembers(Matcher):
+    """Matches if two iterators have the same members.
+
+    This is not the same as set equivalence.  The two iterators must be of the
+    same length and have the same repetitions.
+    """
+
+    def __init__(self, expected):
+        super(SameMembers, self).__init__()
+        self.expected = expected
+
+    def __str__(self):
+        return '%s(%r)' % (self.__class__.__name__, self.expected)
+
+    def match(self, observed):
+        expected_only = list_subtract(self.expected, observed)
+        observed_only = list_subtract(observed, self.expected)
+        if expected_only == observed_only == []:
+            return
+        return PostfixedMismatch(
+            "\nmissing:    %s\nextra:      %s" % (
+                _format(expected_only), _format(observed_only)),
+            _BinaryMismatch(self.expected, 'elements differ', observed))
+
+
+class DoesNotStartWith(Mismatch):
+
+    def __init__(self, matchee, expected):
+        """Create a DoesNotStartWith Mismatch.
+
+        :param matchee: the string that did not match.
+        :param expected: the string that 'matchee' was expected to start with.
+        """
+        self.matchee = matchee
+        self.expected = expected
+
+    def describe(self):
+        return "%s does not start with %s." % (
+            text_repr(self.matchee), text_repr(self.expected))
+
+
+class StartsWith(Matcher):
+    """Checks whether one string starts with another."""
+
+    def __init__(self, expected):
+        """Create a StartsWith Matcher.
+
+        :param expected: the string that matchees should start with.
+        """
+        self.expected = expected
+
+    def __str__(self):
+        return "StartsWith(%r)" % (self.expected,)
+
+    def match(self, matchee):
+        if not matchee.startswith(self.expected):
+            return DoesNotStartWith(matchee, self.expected)
+        return None
+
+
+class DoesNotEndWith(Mismatch):
+
+    def __init__(self, matchee, expected):
+        """Create a DoesNotEndWith Mismatch.
+
+        :param matchee: the string that did not match.
+        :param expected: the string that 'matchee' was expected to end with.
+        """
+        self.matchee = matchee
+        self.expected = expected
+
+    def describe(self):
+        return "%s does not end with %s." % (
+            text_repr(self.matchee), text_repr(self.expected))
+
+
+class EndsWith(Matcher):
+    """Checks whether one string ends with another."""
+
+    def __init__(self, expected):
+        """Create a EndsWith Matcher.
+
+        :param expected: the string that matchees should end with.
+        """
+        self.expected = expected
+
+    def __str__(self):
+        return "EndsWith(%r)" % (self.expected,)
+
+    def match(self, matchee):
+        if not matchee.endswith(self.expected):
+            return DoesNotEndWith(matchee, self.expected)
+        return None
+
+
+class IsInstance(object):
+    """Matcher that wraps isinstance."""
+
+    def __init__(self, *types):
+        self.types = tuple(types)
+
+    def __str__(self):
+        return "%s(%s)" % (self.__class__.__name__,
+                ', '.join(type.__name__ for type in self.types))
+
+    def match(self, other):
+        if isinstance(other, self.types):
+            return None
+        return NotAnInstance(other, self.types)
+
+
+class NotAnInstance(Mismatch):
+
+    def __init__(self, matchee, types):
+        """Create a NotAnInstance Mismatch.
+
+        :param matchee: the thing which is not an instance of any of types.
+        :param types: A tuple of the types which were expected.
+        """
+        self.matchee = matchee
+        self.types = types
+
+    def describe(self):
+        if len(self.types) == 1:
+            typestr = self.types[0].__name__
+        else:
+            typestr = 'any of (%s)' % ', '.join(type.__name__ for type in
+                    self.types)
+        return "'%s' is not an instance of %s" % (self.matchee, typestr)
+
+
+class DoesNotContain(Mismatch):
+
+    def __init__(self, matchee, needle):
+        """Create a DoesNotContain Mismatch.
+
+        :param matchee: the object that did not contain needle.
+        :param needle: the needle that 'matchee' was expected to contain.
+        """
+        self.matchee = matchee
+        self.needle = needle
+
+    def describe(self):
+        return "%r not in %r" % (self.needle, self.matchee)
+
+
+class Contains(Matcher):
+    """Checks whether something is contained in another thing."""
+
+    def __init__(self, needle):
+        """Create a Contains Matcher.
+
+        :param needle: the thing that needs to be contained by matchees.
+        """
+        self.needle = needle
+
+    def __str__(self):
+        return "Contains(%r)" % (self.needle,)
+
+    def match(self, matchee):
+        try:
+            if self.needle not in matchee:
+                return DoesNotContain(matchee, self.needle)
+        except TypeError:
+            # e.g. 1 in 2 will raise TypeError
+            return DoesNotContain(matchee, self.needle)
+        return None
+
+
+class MatchesRegex(object):
+    """Matches if the matchee is matched by a regular expression."""
+
+    def __init__(self, pattern, flags=0):
+        self.pattern = pattern
+        self.flags = flags
+
+    def __str__(self):
+        args = ['%r' % self.pattern]
+        flag_arg = []
+        # dir() sorts the attributes for us, so we don't need to do it again.
+        for flag in dir(re):
+            if len(flag) == 1:
+                if self.flags & getattr(re, flag):
+                    flag_arg.append('re.%s' % flag)
+        if flag_arg:
+            args.append('|'.join(flag_arg))
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(args))
+
+    def match(self, value):
+        if not re.match(self.pattern, value, self.flags):
+            pattern = self.pattern
+            if not isinstance(pattern, str_is_unicode and str or unicode):
+                pattern = pattern.decode("latin1")
+            pattern = pattern.encode("unicode_escape").decode("ascii")
+            return Mismatch("%r does not match /%s/" % (
+                    value, pattern.replace("\\\\", "\\")))

=== renamed file 'testtools/matchers.py' => 'testtools/matchers/_core.py'
--- testtools/matchers.py	2012-08-10 14:05:57 +0000
+++ testtools/matchers/_core.py	2012-09-08 17:30:27 +0000
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2011 testtools developers. See LICENSE for details.
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
 
 """Matchers, a way to express complex assertions outside the testcase.
 
@@ -10,67 +10,19 @@
 $ python -c 'import testtools.matchers; print testtools.matchers.__all__'
 """
 
-__metaclass__ = type
 __all__ = [
-    'AfterPreprocessing',
-    'AllMatch',
-    'Annotate',
-    'Contains',
-    'ContainsAll',
-    'DirExists',
-    'DocTestMatches',
-    'EndsWith',
-    'Equals',
-    'FileContains',
-    'FileExists',
-    'GreaterThan',
-    'HasPermissions',
-    'Is',
-    'IsInstance',
-    'KeysEqual',
-    'LessThan',
-    'MatchesAll',
-    'MatchesAny',
-    'MatchesException',
-    'MatchesListwise',
-    'MatchesPredicate',
-    'MatchesRegex',
-    'MatchesSetwise',
-    'MatchesStructure',
-    'NotEquals',
-    'Not',
-    'PathExists',
-    'Raises',
-    'raises',
-    'SamePath',
-    'StartsWith',
-    'TarballContains',
+    'Matcher',
+    'Mismatch',
+    'MismatchDecorator',
+    'MismatchError',
     ]
 
-import doctest
-import operator
-from pprint import pformat
-import re
-import os
-import sys
-import tarfile
-import types
-
 from testtools.compat import (
-    classtypes,
-    _error_repr,
-    isbaseexception,
     _isbytes,
     istext,
     str_is_unicode,
     text_repr
     )
-from testtools.helpers import (
-    dict_subtract,
-    filter_values,
-    list_subtract,
-    map_values,
-    )
 
 
 class Matcher(object):
@@ -218,1349 +170,6 @@
         return self.original.get_details()
 
 
-def _format(thing):
-    """
-    Blocks of text with newlines are formatted as triple-quote
-    strings. Everything else is pretty-printed.
-    """
-    if istext(thing) or _isbytes(thing):
-        return text_repr(thing)
-    return pformat(thing)
-
-
-class _NonManglingOutputChecker(doctest.OutputChecker):
-    """Doctest checker that works with unicode rather than mangling strings
-
-    This is needed because current Python versions have tried to fix string
-    encoding related problems, but regressed the default behaviour with
-    unicode inputs in the process.
-
-    In Python 2.6 and 2.7 ``OutputChecker.output_difference`` is was changed
-    to return a bytestring encoded as per ``sys.stdout.encoding``, or utf-8 if
-    that can't be determined. Worse, that encoding process happens in the
-    innocent looking `_indent` global function. Because the
-    `DocTestMismatch.describe` result may well not be destined for printing to
-    stdout, this is no good for us. To get a unicode return as before, the
-    method is monkey patched if ``doctest._encoding`` exists.
-
-    Python 3 has a different problem. For some reason both inputs are encoded
-    to ascii with 'backslashreplace', making an escaped string matches its
-    unescaped form. Overriding the offending ``OutputChecker._toAscii`` method
-    is sufficient to revert this.
-    """
-
-    def _toAscii(self, s):
-        """Return ``s`` unchanged rather than mangling it to ascii"""
-        return s
-
-    # Only do this overriding hackery if doctest has a broken _input function
-    if getattr(doctest, "_encoding", None) is not None:
-        from types import FunctionType as __F
-        __f = doctest.OutputChecker.output_difference.im_func
-        __g = dict(__f.func_globals)
-        def _indent(s, indent=4, _pattern=re.compile("^(?!$)", re.MULTILINE)):
-            """Prepend non-empty lines in ``s`` with ``indent`` number of spaces"""
-            return _pattern.sub(indent*" ", s)
-        __g["_indent"] = _indent
-        output_difference = __F(__f.func_code, __g, "output_difference")
-        del __F, __f, __g, _indent
-
-
-class DocTestMatches(object):
-    """See if a string matches a doctest example."""
-
-    def __init__(self, example, flags=0):
-        """Create a DocTestMatches to match example.
-
-        :param example: The example to match e.g. 'foo bar baz'
-        :param flags: doctest comparison flags to match on. e.g.
-            doctest.ELLIPSIS.
-        """
-        if not example.endswith('\n'):
-            example += '\n'
-        self.want = example # required variable name by doctest.
-        self.flags = flags
-        self._checker = _NonManglingOutputChecker()
-
-    def __str__(self):
-        if self.flags:
-            flagstr = ", flags=%d" % self.flags
-        else:
-            flagstr = ""
-        return 'DocTestMatches(%r%s)' % (self.want, flagstr)
-
-    def _with_nl(self, actual):
-        result = self.want.__class__(actual)
-        if not result.endswith('\n'):
-            result += '\n'
-        return result
-
-    def match(self, actual):
-        with_nl = self._with_nl(actual)
-        if self._checker.check_output(self.want, with_nl, self.flags):
-            return None
-        return DocTestMismatch(self, with_nl)
-
-    def _describe_difference(self, with_nl):
-        return self._checker.output_difference(self, with_nl, self.flags)
-
-
-class DocTestMismatch(Mismatch):
-    """Mismatch object for DocTestMatches."""
-
-    def __init__(self, matcher, with_nl):
-        self.matcher = matcher
-        self.with_nl = with_nl
-
-    def describe(self):
-        s = self.matcher._describe_difference(self.with_nl)
-        if str_is_unicode or isinstance(s, unicode):
-            return s
-        # GZ 2011-08-24: This is actually pretty bogus, most C0 codes should
-        #                be escaped, in addition to non-ascii bytes.
-        return s.decode("latin1").encode("ascii", "backslashreplace")
-
-
-class DoesNotContain(Mismatch):
-
-    def __init__(self, matchee, needle):
-        """Create a DoesNotContain Mismatch.
-
-        :param matchee: the object that did not contain needle.
-        :param needle: the needle that 'matchee' was expected to contain.
-        """
-        self.matchee = matchee
-        self.needle = needle
-
-    def describe(self):
-        return "%r not in %r" % (self.needle, self.matchee)
-
-
-class DoesNotStartWith(Mismatch):
-
-    def __init__(self, matchee, expected):
-        """Create a DoesNotStartWith Mismatch.
-
-        :param matchee: the string that did not match.
-        :param expected: the string that 'matchee' was expected to start with.
-        """
-        self.matchee = matchee
-        self.expected = expected
-
-    def describe(self):
-        return "%s does not start with %s." % (
-            text_repr(self.matchee), text_repr(self.expected))
-
-
-class DoesNotEndWith(Mismatch):
-
-    def __init__(self, matchee, expected):
-        """Create a DoesNotEndWith Mismatch.
-
-        :param matchee: the string that did not match.
-        :param expected: the string that 'matchee' was expected to end with.
-        """
-        self.matchee = matchee
-        self.expected = expected
-
-    def describe(self):
-        return "%s does not end with %s." % (
-            text_repr(self.matchee), text_repr(self.expected))
-
-
-class _BinaryComparison(object):
-    """Matcher that compares an object to another object."""
-
-    def __init__(self, expected):
-        self.expected = expected
-
-    def __str__(self):
-        return "%s(%r)" % (self.__class__.__name__, self.expected)
-
-    def match(self, other):
-        if self.comparator(other, self.expected):
-            return None
-        return _BinaryMismatch(self.expected, self.mismatch_string, other)
-
-    def comparator(self, expected, other):
-        raise NotImplementedError(self.comparator)
-
-
-class _BinaryMismatch(Mismatch):
-    """Two things did not match."""
-
-    def __init__(self, expected, mismatch_string, other):
-        self.expected = expected
-        self._mismatch_string = mismatch_string
-        self.other = other
-
-    def describe(self):
-        left = repr(self.expected)
-        right = repr(self.other)
-        if len(left) + len(right) > 70:
-            return "%s:\nreference = %s\nactual    = %s\n" % (
-                self._mismatch_string, _format(self.expected),
-                _format(self.other))
-        else:
-            return "%s %s %s" % (left, self._mismatch_string, right)
-
-
-class MatchesPredicate(Matcher):
-    """Match if a given function returns True.
-
-    It is reasonably common to want to make a very simple matcher based on a
-    function that you already have that returns True or False given a single
-    argument (i.e. a predicate function).  This matcher makes it very easy to
-    do so. e.g.::
-
-      IsEven = MatchesPredicate(lambda x: x % 2 == 0, '%s is not even')
-      self.assertThat(4, IsEven)
-    """
-
-    def __init__(self, predicate, message):
-        """Create a ``MatchesPredicate`` matcher.
-
-        :param predicate: A function that takes a single argument and returns
-            a value that will be interpreted as a boolean.
-        :param message: A message to describe a mismatch.  It will be formatted
-            with '%' and be given whatever was passed to ``match()``. Thus, it
-            needs to contain exactly one thing like '%s', '%d' or '%f'.
-        """
-        self.predicate = predicate
-        self.message = message
-
-    def __str__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__, self.predicate, self.message)
-
-    def match(self, x):
-        if not self.predicate(x):
-            return Mismatch(self.message % x)
-
-
-class Equals(_BinaryComparison):
-    """Matches if the items are equal."""
-
-    comparator = operator.eq
-    mismatch_string = '!='
-
-
-class NotEquals(_BinaryComparison):
-    """Matches if the items are not equal.
-
-    In most cases, this is equivalent to ``Not(Equals(foo))``. The difference
-    only matters when testing ``__ne__`` implementations.
-    """
-
-    comparator = operator.ne
-    mismatch_string = '=='
-
-
-class Is(_BinaryComparison):
-    """Matches if the items are identical."""
-
-    comparator = operator.is_
-    mismatch_string = 'is not'
-
-
-class IsInstance(object):
-    """Matcher that wraps isinstance."""
-
-    def __init__(self, *types):
-        self.types = tuple(types)
-
-    def __str__(self):
-        return "%s(%s)" % (self.__class__.__name__,
-                ', '.join(type.__name__ for type in self.types))
-
-    def match(self, other):
-        if isinstance(other, self.types):
-            return None
-        return NotAnInstance(other, self.types)
-
-
-class NotAnInstance(Mismatch):
-
-    def __init__(self, matchee, types):
-        """Create a NotAnInstance Mismatch.
-
-        :param matchee: the thing which is not an instance of any of types.
-        :param types: A tuple of the types which were expected.
-        """
-        self.matchee = matchee
-        self.types = types
-
-    def describe(self):
-        if len(self.types) == 1:
-            typestr = self.types[0].__name__
-        else:
-            typestr = 'any of (%s)' % ', '.join(type.__name__ for type in
-                    self.types)
-        return "'%s' is not an instance of %s" % (self.matchee, typestr)
-
-
-class LessThan(_BinaryComparison):
-    """Matches if the item is less than the matchers reference object."""
-
-    comparator = operator.__lt__
-    mismatch_string = 'is not >'
-
-
-class GreaterThan(_BinaryComparison):
-    """Matches if the item is greater than the matchers reference object."""
-
-    comparator = operator.__gt__
-    mismatch_string = 'is not <'
-
-
-class MatchesAny(object):
-    """Matches if any of the matchers it is created with match."""
-
-    def __init__(self, *matchers):
-        self.matchers = matchers
-
-    def match(self, matchee):
-        results = []
-        for matcher in self.matchers:
-            mismatch = matcher.match(matchee)
-            if mismatch is None:
-                return None
-            results.append(mismatch)
-        return MismatchesAll(results)
-
-    def __str__(self):
-        return "MatchesAny(%s)" % ', '.join([
-            str(matcher) for matcher in self.matchers])
-
-
-class MatchesAll(object):
-    """Matches if all of the matchers it is created with match."""
-
-    def __init__(self, *matchers, **options):
-        """Construct a MatchesAll matcher.
-
-        Just list the component matchers as arguments in the ``*args``
-        style. If you want only the first mismatch to be reported, past in
-        first_only=True as a keyword argument. By default, all mismatches are
-        reported.
-        """
-        self.matchers = matchers
-        self.first_only = options.get('first_only', False)
-
-    def __str__(self):
-        return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers))
-
-    def match(self, matchee):
-        results = []
-        for matcher in self.matchers:
-            mismatch = matcher.match(matchee)
-            if mismatch is not None:
-                if self.first_only:
-                    return mismatch
-                results.append(mismatch)
-        if results:
-            return MismatchesAll(results)
-        else:
-            return None
-
-
-class MismatchesAll(Mismatch):
-    """A mismatch with many child mismatches."""
-
-    def __init__(self, mismatches, wrap=True):
-        self.mismatches = mismatches
-        self._wrap = wrap
-
-    def describe(self):
-        descriptions = []
-        if self._wrap:
-            descriptions = ["Differences: ["]
-        for mismatch in self.mismatches:
-            descriptions.append(mismatch.describe())
-        if self._wrap:
-            descriptions.append("]")
-        return '\n'.join(descriptions)
-
-
-class MatchesAllDict(Matcher):
-    """Matches if all of the matchers it is created with match.
-
-    A lot like ``MatchesAll``, but takes a dict of Matchers and labels any
-    mismatches with the key of the dictionary.
-    """
-
-    def __init__(self, matchers):
-        super(MatchesAllDict, self).__init__()
-        self.matchers = matchers
-
-    def __str__(self):
-        return 'MatchesAllDict({%s})' % (
-            ', '.join('%r: %s' % (k, v) for k, v in self.matchers.items()))
-
-    def match(self, observed):
-        mismatches = {}
-        for label in self.matchers:
-            mismatches[label] = self.matchers[label].match(observed)
-        return _dict_to_mismatch(
-            mismatches, result_mismatch=LabelledMismatches)
-
-
-class DictMismatches(Mismatch):
-    """A mismatch with a dict of child mismatches."""
-
-    def __init__(self, mismatches, details=None):
-        super(DictMismatches, self).__init__(None, details=details)
-        self.mismatches = mismatches
-
-    def describe(self):
-        lines = ['{']
-        lines.extend(
-            ['  %r: %s,' % (key, mismatch.describe())
-             for (key, mismatch) in sorted(self.mismatches.items())])
-        lines.append('}')
-        return '\n'.join(lines)
-
-
-def LabelledMismatches(mismatches, details=None):
-    """A collection of mismatches, each labelled."""
-    return MismatchesAll(
-        (PrefixedMismatch(k, v) for (k, v) in sorted(mismatches.items())),
-        wrap=False)
-
-
-def _dict_to_mismatch(data, to_mismatch=None,
-                      result_mismatch=DictMismatches):
-    if to_mismatch:
-        data = map_values(to_mismatch, data)
-    mismatches = filter_values(bool, data)
-    if mismatches:
-        return result_mismatch(mismatches)
-
-
-class Not(object):
-    """Inverts a matcher."""
-
-    def __init__(self, matcher):
-        self.matcher = matcher
-
-    def __str__(self):
-        return 'Not(%s)' % (self.matcher,)
-
-    def match(self, other):
-        mismatch = self.matcher.match(other)
-        if mismatch is None:
-            return MatchedUnexpectedly(self.matcher, other)
-        else:
-            return None
-
-
-class MatchedUnexpectedly(Mismatch):
-    """A thing matched when it wasn't supposed to."""
-
-    def __init__(self, matcher, other):
-        self.matcher = matcher
-        self.other = other
-
-    def describe(self):
-        return "%r matches %s" % (self.other, self.matcher)
-
-
-class MatchesException(Matcher):
-    """Match an exc_info tuple against an exception instance or type."""
-
-    def __init__(self, exception, value_re=None):
-        """Create a MatchesException that will match exc_info's for exception.
-
-        :param exception: Either an exception instance or type.
-            If an instance is given, the type and arguments of the exception
-            are checked. If a type is given only the type of the exception is
-            checked. If a tuple is given, then as with isinstance, any of the
-            types in the tuple matching is sufficient to match.
-        :param value_re: If 'exception' is a type, and the matchee exception
-            is of the right type, then match against this.  If value_re is a
-            string, then assume value_re is a regular expression and match
-            the str() of the exception against it.  Otherwise, assume value_re
-            is a matcher, and match the exception against it.
-        """
-        Matcher.__init__(self)
-        self.expected = exception
-        if istext(value_re):
-            value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
-        self.value_re = value_re
-        self._is_instance = type(self.expected) not in classtypes() + (tuple,)
-
-    def match(self, other):
-        if type(other) != tuple:
-            return Mismatch('%r is not an exc_info tuple' % other)
-        expected_class = self.expected
-        if self._is_instance:
-            expected_class = expected_class.__class__
-        if not issubclass(other[0], expected_class):
-            return Mismatch('%r is not a %r' % (other[0], expected_class))
-        if self._is_instance:
-            if other[1].args != self.expected.args:
-                return Mismatch('%s has different arguments to %s.' % (
-                        _error_repr(other[1]), _error_repr(self.expected)))
-        elif self.value_re is not None:
-            return self.value_re.match(other[1])
-
-    def __str__(self):
-        if self._is_instance:
-            return "MatchesException(%s)" % _error_repr(self.expected)
-        return "MatchesException(%s)" % repr(self.expected)
-
-
-class Contains(Matcher):
-    """Checks whether something is contained in another thing."""
-
-    def __init__(self, needle):
-        """Create a Contains Matcher.
-
-        :param needle: the thing that needs to be contained by matchees.
-        """
-        self.needle = needle
-
-    def __str__(self):
-        return "Contains(%r)" % (self.needle,)
-
-    def match(self, matchee):
-        try:
-            if self.needle not in matchee:
-                return DoesNotContain(matchee, self.needle)
-        except TypeError:
-            # e.g. 1 in 2 will raise TypeError
-            return DoesNotContain(matchee, self.needle)
-        return None
-
-
-def ContainsAll(items):
-    """Make a matcher that checks whether a list of things is contained
-    in another thing.
-
-    The matcher effectively checks that the provided sequence is a subset of
-    the matchee.
-    """
-    return MatchesAll(*map(Contains, items), first_only=False)
-
-
-class StartsWith(Matcher):
-    """Checks whether one string starts with another."""
-
-    def __init__(self, expected):
-        """Create a StartsWith Matcher.
-
-        :param expected: the string that matchees should start with.
-        """
-        self.expected = expected
-
-    def __str__(self):
-        return "StartsWith(%r)" % (self.expected,)
-
-    def match(self, matchee):
-        if not matchee.startswith(self.expected):
-            return DoesNotStartWith(matchee, self.expected)
-        return None
-
-
-class EndsWith(Matcher):
-    """Checks whether one string ends with another."""
-
-    def __init__(self, expected):
-        """Create a EndsWith Matcher.
-
-        :param expected: the string that matchees should end with.
-        """
-        self.expected = expected
-
-    def __str__(self):
-        return "EndsWith(%r)" % (self.expected,)
-
-    def match(self, matchee):
-        if not matchee.endswith(self.expected):
-            return DoesNotEndWith(matchee, self.expected)
-        return None
-
-
-class KeysEqual(Matcher):
-    """Checks whether a dict has particular keys."""
-
-    def __init__(self, *expected):
-        """Create a `KeysEqual` Matcher.
-
-        :param expected: The keys the dict is expected to have.  If a dict,
-            then we use the keys of that dict, if a collection, we assume it
-            is a collection of expected keys.
-        """
-        super(KeysEqual, self).__init__()
-        try:
-            self.expected = expected.keys()
-        except AttributeError:
-            self.expected = list(expected)
-
-    def __str__(self):
-        return "KeysEqual(%s)" % ', '.join(map(repr, self.expected))
-
-    def match(self, matchee):
-        expected = sorted(self.expected)
-        matched = Equals(expected).match(sorted(matchee.keys()))
-        if matched:
-            return AnnotatedMismatch(
-                'Keys not equal',
-                _BinaryMismatch(expected, 'does not match', matchee))
-        return None
-
-
-class Annotate(object):
-    """Annotates a matcher with a descriptive string.
-
-    Mismatches are then described as '<mismatch>: <annotation>'.
-    """
-
-    def __init__(self, annotation, matcher):
-        self.annotation = annotation
-        self.matcher = matcher
-
-    @classmethod
-    def if_message(cls, annotation, matcher):
-        """Annotate ``matcher`` only if ``annotation`` is non-empty."""
-        if not annotation:
-            return matcher
-        return cls(annotation, matcher)
-
-    def __str__(self):
-        return 'Annotate(%r, %s)' % (self.annotation, self.matcher)
-
-    def match(self, other):
-        mismatch = self.matcher.match(other)
-        if mismatch is not None:
-            return AnnotatedMismatch(self.annotation, mismatch)
-
-
-class PostfixedMismatch(MismatchDecorator):
-    """A mismatch annotated with a descriptive string."""
-
-    def __init__(self, annotation, mismatch):
-        super(PostfixedMismatch, self).__init__(mismatch)
-        self.annotation = annotation
-        self.mismatch = mismatch
-
-    def describe(self):
-        return '%s: %s' % (self.original.describe(), self.annotation)
-
-
-AnnotatedMismatch = PostfixedMismatch
-
-
-class PrefixedMismatch(MismatchDecorator):
-
-    def __init__(self, prefix, mismatch):
-        super(PrefixedMismatch, self).__init__(mismatch)
-        self.prefix = prefix
-
-    def describe(self):
-        return '%s: %s' % (self.prefix, self.original.describe())
-
-
-class Raises(Matcher):
-    """Match if the matchee raises an exception when called.
-
-    Exceptions which are not subclasses of Exception propogate out of the
-    Raises.match call unless they are explicitly matched.
-    """
-
-    def __init__(self, exception_matcher=None):
-        """Create a Raises matcher.
-
-        :param exception_matcher: Optional validator for the exception raised
-            by matchee. If supplied the exc_info tuple for the exception raised
-            is passed into that matcher. If no exception_matcher is supplied
-            then the simple fact of raising an exception is considered enough
-            to match on.
-        """
-        self.exception_matcher = exception_matcher
-
-    def match(self, matchee):
-        try:
-            result = matchee()
-            return Mismatch('%r returned %r' % (matchee, result))
-        # Catch all exceptions: Raises() should be able to match a
-        # KeyboardInterrupt or SystemExit.
-        except:
-            exc_info = sys.exc_info()
-            if self.exception_matcher:
-                mismatch = self.exception_matcher.match(exc_info)
-                if not mismatch:
-                    del exc_info
-                    return
-            else:
-                mismatch = None
-            # The exception did not match, or no explicit matching logic was
-            # performed. If the exception is a non-user exception (that is, not
-            # a subclass of Exception on Python 2.5+) then propogate it.
-            if isbaseexception(exc_info[1]):
-                del exc_info
-                raise
-            return mismatch
-
-    def __str__(self):
-        return 'Raises()'
-
-
-def raises(exception):
-    """Make a matcher that checks that a callable raises an exception.
-
-    This is a convenience function, exactly equivalent to::
-
-        return Raises(MatchesException(exception))
-
-    See `Raises` and `MatchesException` for more information.
-    """
-    return Raises(MatchesException(exception))
-
-
-class MatchesListwise(object):
-    """Matches if each matcher matches the corresponding value.
-
-    More easily explained by example than in words:
-
-    >>> MatchesListwise([Equals(1)]).match([1])
-    >>> MatchesListwise([Equals(1), Equals(2)]).match([1, 2])
-    >>> print (MatchesListwise([Equals(1), Equals(2)]).match([2, 1]).describe())
-    Differences: [
-    1 != 2
-    2 != 1
-    ]
-    >>> matcher = MatchesListwise([Equals(1), Equals(2)], first_only=True)
-    >>> print (matcher.match([3, 4]).describe())
-    1 != 3
-    """
-
-    def __init__(self, matchers, first_only=False):
-        """Construct a MatchesListwise matcher.
-
-        :param matchers: A list of matcher that the matched values must match.
-        :param first_only: If True, then only report the first mismatch,
-            otherwise report all of them. Defaults to False.
-        """
-        self.matchers = matchers
-        self.first_only = first_only
-
-    def match(self, values):
-        mismatches = []
-        length_mismatch = Annotate(
-            "Length mismatch", Equals(len(self.matchers))).match(len(values))
-        if length_mismatch:
-            mismatches.append(length_mismatch)
-        for matcher, value in zip(self.matchers, values):
-            mismatch = matcher.match(value)
-            if mismatch:
-                if self.first_only:
-                    return mismatch
-                mismatches.append(mismatch)
-        if mismatches:
-            return MismatchesAll(mismatches)
-
-
-class MatchesStructure(object):
-    """Matcher that matches an object structurally.
-
-    'Structurally' here means that attributes of the object being matched are
-    compared against given matchers.
-
-    `fromExample` allows the creation of a matcher from a prototype object and
-    then modified versions can be created with `update`.
-
-    `byEquality` creates a matcher in much the same way as the constructor,
-    except that the matcher for each of the attributes is assumed to be
-    `Equals`.
-
-    `byMatcher` creates a similar matcher to `byEquality`, but you get to pick
-    the matcher, rather than just using `Equals`.
-    """
-
-    def __init__(self, **kwargs):
-        """Construct a `MatchesStructure`.
-
-        :param kwargs: A mapping of attributes to matchers.
-        """
-        self.kws = kwargs
-
-    @classmethod
-    def byEquality(cls, **kwargs):
-        """Matches an object where the attributes equal the keyword values.
-
-        Similar to the constructor, except that the matcher is assumed to be
-        Equals.
-        """
-        return cls.byMatcher(Equals, **kwargs)
-
-    @classmethod
-    def byMatcher(cls, matcher, **kwargs):
-        """Matches an object where the attributes match the keyword values.
-
-        Similar to the constructor, except that the provided matcher is used
-        to match all of the values.
-        """
-        return cls(**map_values(matcher, kwargs))
-
-    @classmethod
-    def fromExample(cls, example, *attributes):
-        kwargs = {}
-        for attr in attributes:
-            kwargs[attr] = Equals(getattr(example, attr))
-        return cls(**kwargs)
-
-    def update(self, **kws):
-        new_kws = self.kws.copy()
-        for attr, matcher in kws.items():
-            if matcher is None:
-                new_kws.pop(attr, None)
-            else:
-                new_kws[attr] = matcher
-        return type(self)(**new_kws)
-
-    def __str__(self):
-        kws = []
-        for attr, matcher in sorted(self.kws.items()):
-            kws.append("%s=%s" % (attr, matcher))
-        return "%s(%s)" % (self.__class__.__name__, ', '.join(kws))
-
-    def match(self, value):
-        matchers = []
-        values = []
-        for attr, matcher in sorted(self.kws.items()):
-            matchers.append(Annotate(attr, matcher))
-            values.append(getattr(value, attr))
-        return MatchesListwise(matchers).match(values)
-
-
-class MatchesRegex(object):
-    """Matches if the matchee is matched by a regular expression."""
-
-    def __init__(self, pattern, flags=0):
-        self.pattern = pattern
-        self.flags = flags
-
-    def __str__(self):
-        args = ['%r' % self.pattern]
-        flag_arg = []
-        # dir() sorts the attributes for us, so we don't need to do it again.
-        for flag in dir(re):
-            if len(flag) == 1:
-                if self.flags & getattr(re, flag):
-                    flag_arg.append('re.%s' % flag)
-        if flag_arg:
-            args.append('|'.join(flag_arg))
-        return '%s(%s)' % (self.__class__.__name__, ', '.join(args))
-
-    def match(self, value):
-        if not re.match(self.pattern, value, self.flags):
-            pattern = self.pattern
-            if not isinstance(pattern, str_is_unicode and str or unicode):
-                pattern = pattern.decode("latin1")
-            pattern = pattern.encode("unicode_escape").decode("ascii")
-            return Mismatch("%r does not match /%s/" % (
-                    value, pattern.replace("\\\\", "\\")))
-
-
-class MatchesSetwise(object):
-    """Matches if all the matchers match elements of the value being matched.
-
-    That is, each element in the 'observed' set must match exactly one matcher
-    from the set of matchers, with no matchers left over.
-
-    The difference compared to `MatchesListwise` is that the order of the
-    matchings does not matter.
-    """
-
-    def __init__(self, *matchers):
-        self.matchers = matchers
-
-    def match(self, observed):
-        remaining_matchers = set(self.matchers)
-        not_matched = []
-        for value in observed:
-            for matcher in remaining_matchers:
-                if matcher.match(value) is None:
-                    remaining_matchers.remove(matcher)
-                    break
-            else:
-                not_matched.append(value)
-        if not_matched or remaining_matchers:
-            remaining_matchers = list(remaining_matchers)
-            # There are various cases that all should be reported somewhat
-            # differently.
-
-            # There are two trivial cases:
-            # 1) There are just some matchers left over.
-            # 2) There are just some values left over.
-
-            # Then there are three more interesting cases:
-            # 3) There are the same number of matchers and values left over.
-            # 4) There are more matchers left over than values.
-            # 5) There are more values left over than matchers.
-
-            if len(not_matched) == 0:
-                if len(remaining_matchers) > 1:
-                    msg = "There were %s matchers left over: " % (
-                        len(remaining_matchers),)
-                else:
-                    msg = "There was 1 matcher left over: "
-                msg += ', '.join(map(str, remaining_matchers))
-                return Mismatch(msg)
-            elif len(remaining_matchers) == 0:
-                if len(not_matched) > 1:
-                    return Mismatch(
-                        "There were %s values left over: %s" % (
-                            len(not_matched), not_matched))
-                else:
-                    return Mismatch(
-                        "There was 1 value left over: %s" % (
-                            not_matched, ))
-            else:
-                common_length = min(len(remaining_matchers), len(not_matched))
-                if common_length == 0:
-                    raise AssertionError("common_length can't be 0 here")
-                if common_length > 1:
-                    msg = "There were %s mismatches" % (common_length,)
-                else:
-                    msg = "There was 1 mismatch"
-                if len(remaining_matchers) > len(not_matched):
-                    extra_matchers = remaining_matchers[common_length:]
-                    msg += " and %s extra matcher" % (len(extra_matchers), )
-                    if len(extra_matchers) > 1:
-                        msg += "s"
-                    msg += ': ' + ', '.join(map(str, extra_matchers))
-                elif len(not_matched) > len(remaining_matchers):
-                    extra_values = not_matched[common_length:]
-                    msg += " and %s extra value" % (len(extra_values), )
-                    if len(extra_values) > 1:
-                        msg += "s"
-                    msg += ': ' + str(extra_values)
-                return Annotate(
-                    msg, MatchesListwise(remaining_matchers[:common_length])
-                    ).match(not_matched[:common_length])
-
-
-class AfterPreprocessing(object):
-    """Matches if the value matches after passing through a function.
-
-    This can be used to aid in creating trivial matchers as functions, for
-    example::
-
-      def PathHasFileContent(content):
-          def _read(path):
-              return open(path).read()
-          return AfterPreprocessing(_read, Equals(content))
-    """
-
-    def __init__(self, preprocessor, matcher, annotate=True):
-        """Create an AfterPreprocessing matcher.
-
-        :param preprocessor: A function called with the matchee before
-            matching.
-        :param matcher: What to match the preprocessed matchee against.
-        :param annotate: Whether or not to annotate the matcher with
-            something explaining how we transformed the matchee. Defaults
-            to True.
-        """
-        self.preprocessor = preprocessor
-        self.matcher = matcher
-        self.annotate = annotate
-
-    def _str_preprocessor(self):
-        if isinstance(self.preprocessor, types.FunctionType):
-            return '<function %s>' % self.preprocessor.__name__
-        return str(self.preprocessor)
-
-    def __str__(self):
-        return "AfterPreprocessing(%s, %s)" % (
-            self._str_preprocessor(), self.matcher)
-
-    def match(self, value):
-        after = self.preprocessor(value)
-        if self.annotate:
-            matcher = Annotate(
-                "after %s on %r" % (self._str_preprocessor(), value),
-                self.matcher)
-        else:
-            matcher = self.matcher
-        return matcher.match(after)
-
-# This is the old, deprecated. spelling of the name, kept for backwards
-# compatibility.
-AfterPreproccessing = AfterPreprocessing
-
-
-class AllMatch(object):
-    """Matches if all provided values match the given matcher."""
-
-    def __init__(self, matcher):
-        self.matcher = matcher
-
-    def __str__(self):
-        return 'AllMatch(%s)' % (self.matcher,)
-
-    def match(self, values):
-        mismatches = []
-        for value in values:
-            mismatch = self.matcher.match(value)
-            if mismatch:
-                mismatches.append(mismatch)
-        if mismatches:
-            return MismatchesAll(mismatches)
-
-
-def PathExists():
-    """Matches if the given path exists.
-
-    Use like this::
-
-      assertThat('/some/path', PathExists())
-    """
-    return MatchesPredicate(os.path.exists, "%s does not exist.")
-
-
-def DirExists():
-    """Matches if the path exists and is a directory."""
-    return MatchesAll(
-        PathExists(),
-        MatchesPredicate(os.path.isdir, "%s is not a directory."),
-        first_only=True)
-
-
-def FileExists():
-    """Matches if the given path exists and is a file."""
-    return MatchesAll(
-        PathExists(),
-        MatchesPredicate(os.path.isfile, "%s is not a file."),
-        first_only=True)
-
-
-class DirContains(Matcher):
-    """Matches if the given directory contains files with the given names.
-
-    That is, is the directory listing exactly equal to the given files?
-    """
-
-    def __init__(self, filenames=None, matcher=None):
-        """Construct a ``DirContains`` matcher.
-
-        Can be used in a basic mode where the whole directory listing is
-        matched against an expected directory listing (by passing
-        ``filenames``).  Can also be used in a more advanced way where the
-        whole directory listing is matched against an arbitrary matcher (by
-        passing ``matcher`` instead).
-
-        :param filenames: If specified, match the sorted directory listing
-            against this list of filenames, sorted.
-        :param matcher: If specified, match the sorted directory listing
-            against this matcher.
-        """
-        if filenames == matcher == None:
-            raise AssertionError(
-                "Must provide one of `filenames` or `matcher`.")
-        if None not in (filenames, matcher):
-            raise AssertionError(
-                "Must provide either `filenames` or `matcher`, not both.")
-        if filenames is None:
-            self.matcher = matcher
-        else:
-            self.matcher = Equals(sorted(filenames))
-
-    def match(self, path):
-        mismatch = DirExists().match(path)
-        if mismatch is not None:
-            return mismatch
-        return self.matcher.match(sorted(os.listdir(path)))
-
-
-class FileContains(Matcher):
-    """Matches if the given file has the specified contents."""
-
-    def __init__(self, contents=None, matcher=None):
-        """Construct a ``FileContains`` matcher.
-
-        Can be used in a basic mode where the file contents are compared for
-        equality against the expected file contents (by passing ``contents``).
-        Can also be used in a more advanced way where the file contents are
-        matched against an arbitrary matcher (by passing ``matcher`` instead).
-
-        :param contents: If specified, match the contents of the file with
-            these contents.
-        :param matcher: If specified, match the contents of the file against
-            this matcher.
-        """
-        if contents == matcher == None:
-            raise AssertionError(
-                "Must provide one of `contents` or `matcher`.")
-        if None not in (contents, matcher):
-            raise AssertionError(
-                "Must provide either `contents` or `matcher`, not both.")
-        if matcher is None:
-            self.matcher = Equals(contents)
-        else:
-            self.matcher = matcher
-
-    def match(self, path):
-        mismatch = PathExists().match(path)
-        if mismatch is not None:
-            return mismatch
-        f = open(path)
-        try:
-            actual_contents = f.read()
-            return self.matcher.match(actual_contents)
-        finally:
-            f.close()
-
-    def __str__(self):
-        return "File at path exists and contains %s" % self.contents
-
-
-class TarballContains(Matcher):
-    """Matches if the given tarball contains the given paths.
-
-    Uses TarFile.getnames() to get the paths out of the tarball.
-    """
-
-    def __init__(self, paths):
-        super(TarballContains, self).__init__()
-        self.paths = paths
-        self.path_matcher = Equals(sorted(self.paths))
-
-    def match(self, tarball_path):
-        # Open underlying file first to ensure it's always closed:
-        # <http://bugs.python.org/issue10233>
-        f = open(tarball_path, "rb")
-        try:
-            tarball = tarfile.open(tarball_path, fileobj=f)
-            try:
-                return self.path_matcher.match(sorted(tarball.getnames()))
-            finally:
-                tarball.close()
-        finally:
-            f.close()
-
-
-class SamePath(Matcher):
-    """Matches if two paths are the same.
-
-    That is, the paths are equal, or they point to the same file but in
-    different ways.  The paths do not have to exist.
-    """
-
-    def __init__(self, path):
-        super(SamePath, self).__init__()
-        self.path = path
-
-    def match(self, other_path):
-        f = lambda x: os.path.abspath(os.path.realpath(x))
-        return Equals(f(self.path)).match(f(other_path))
-
-
-class SameMembers(Matcher):
-    """Matches if two iterators have the same members.
-
-    This is not the same as set equivalence.  The two iterators must be of the
-    same length and have the same repetitions.
-    """
-
-    def __init__(self, expected):
-        super(SameMembers, self).__init__()
-        self.expected = expected
-
-    def __str__(self):
-        return '%s(%r)' % (self.__class__.__name__, self.expected)
-
-    def match(self, observed):
-        expected_only = list_subtract(self.expected, observed)
-        observed_only = list_subtract(observed, self.expected)
-        if expected_only == observed_only == []:
-            return
-        return PostfixedMismatch(
-            "\nmissing:    %s\nextra:      %s" % (
-                _format(expected_only), _format(observed_only)),
-            _BinaryMismatch(self.expected, 'elements differ', observed))
-
-
-class HasPermissions(Matcher):
-    """Matches if a file has the given permissions.
-
-    Permissions are specified and matched as a four-digit octal string.
-    """
-
-    def __init__(self, octal_permissions):
-        """Construct a HasPermissions matcher.
-
-        :param octal_permissions: A four digit octal string, representing the
-            intended access permissions. e.g. '0775' for rwxrwxr-x.
-        """
-        super(HasPermissions, self).__init__()
-        self.octal_permissions = octal_permissions
-
-    def match(self, filename):
-        permissions = oct(os.stat(filename).st_mode)[-4:]
-        return Equals(self.octal_permissions).match(permissions)
-
-
-class _MatchCommonKeys(Matcher):
-    """Match on keys in a dictionary.
-
-    Given a dictionary where the values are matchers, this will look for
-    common keys in the matched dictionary and match if and only if all common
-    keys match the given matchers.
-
-    Thus::
-
-      >>> structure = {'a': Equals('x'), 'b': Equals('y')}
-      >>> _MatchCommonKeys(structure).match({'a': 'x', 'c': 'z'})
-      None
-    """
-
-    def __init__(self, dict_of_matchers):
-        super(_MatchCommonKeys, self).__init__()
-        self._matchers = dict_of_matchers
-
-    def _compare_dicts(self, expected, observed):
-        common_keys = set(expected.keys()) & set(observed.keys())
-        mismatches = {}
-        for key in common_keys:
-            mismatch = expected[key].match(observed[key])
-            if mismatch:
-                mismatches[key] = mismatch
-        return mismatches
-
-    def match(self, observed):
-        mismatches = self._compare_dicts(self._matchers, observed)
-        if mismatches:
-            return DictMismatches(mismatches)
-
-
-class _SubDictOf(Matcher):
-    """Matches if the matched dict only has keys that are in given dict."""
-
-    def __init__(self, super_dict, format_value=repr):
-        super(_SubDictOf, self).__init__()
-        self.super_dict = super_dict
-        self.format_value = format_value
-
-    def match(self, observed):
-        excess = dict_subtract(observed, self.super_dict)
-        return _dict_to_mismatch(
-            excess, lambda v: Mismatch(self.format_value(v)))
-
-
-class _SuperDictOf(Matcher):
-    """Matches if all of the keys in the given dict are in the matched dict.
-    """
-
-    def __init__(self, sub_dict, format_value=repr):
-        super(_SuperDictOf, self).__init__()
-        self.sub_dict = sub_dict
-        self.format_value = format_value
-
-    def match(self, super_dict):
-        return _SubDictOf(super_dict, self.format_value).match(self.sub_dict)
-
-
-def _format_matcher_dict(matchers):
-    return '{%s}' % (
-        ', '.join('%r: %s' % (k, v) for k, v in matchers.items()))
-
-
-class _CombinedMatcher(Matcher):
-    """Many matchers labelled and combined into one uber-matcher.
-
-    Subclass this and then specify a dict of matcher factories that take a
-    single 'expected' value and return a matcher.  The subclass will match
-    only if all of the matchers made from factories match.
-
-    Not **entirely** dissimilar from ``MatchesAll``.
-    """
-
-    matcher_factories = {}
-
-    def __init__(self, expected):
-        super(_CombinedMatcher, self).__init__()
-        self._expected = expected
-
-    def format_expected(self, expected):
-        return repr(expected)
-
-    def __str__(self):
-        return '%s(%s)' % (
-            self.__class__.__name__, self.format_expected(self._expected))
-
-    def match(self, observed):
-        matchers = dict(
-            (k, v(self._expected)) for k, v in self.matcher_factories.items())
-        return MatchesAllDict(matchers).match(observed)
-
-
-class MatchesDict(_CombinedMatcher):
-    """Match a dictionary exactly, by its keys.
-
-    Specify a dictionary mapping keys (often strings) to matchers.  This is
-    the 'expected' dict.  Any dictionary that matches this must have exactly
-    the same keys, and the values must match the corresponding matchers in the
-    expected dict.
-    """
-
-    matcher_factories = {
-        'Extra': _SubDictOf,
-        'Missing': lambda m: _SuperDictOf(m, format_value=str),
-        'Differences': _MatchCommonKeys,
-        }
-
-    format_expected = lambda self, expected: _format_matcher_dict(expected)
-
-
-class ContainsDict(_CombinedMatcher):
-    """Match a dictionary for that contains a specified sub-dictionary.
-
-    Specify a dictionary mapping keys (often strings) to matchers.  This is
-    the 'expected' dict.  Any dictionary that matches this must have **at
-    least** these keys, and the values must match the corresponding matchers
-    in the expected dict.  Dictionaries that have more keys will also match.
-
-    In other words, any matching dictionary must contain the dictionary given
-    to the constructor.
-
-    Does not check for strict sub-dictionary.  That is, equal dictionaries
-    match.
-    """
-
-    matcher_factories = {
-        'Missing': lambda m: _SuperDictOf(m, format_value=str),
-        'Differences': _MatchCommonKeys,
-        }
-
-    format_expected = lambda self, expected: _format_matcher_dict(expected)
-
-
-class ContainedByDict(_CombinedMatcher):
-    """Match a dictionary for which this is a super-dictionary.
-
-    Specify a dictionary mapping keys (often strings) to matchers.  This is
-    the 'expected' dict.  Any dictionary that matches this must have **only**
-    these keys, and the values must match the corresponding matchers in the
-    expected dict.  Dictionaries that have fewer keys can also match.
-
-    In other words, any matching dictionary must be contained by the
-    dictionary given to the constructor.
-
-    Does not check for strict super-dictionary.  That is, equal dictionaries
-    match.
-    """
-
-    matcher_factories = {
-        'Extra': _SubDictOf,
-        'Differences': _MatchCommonKeys,
-        }
-
-    format_expected = lambda self, expected: _format_matcher_dict(expected)
-
-
 # Signal that this is part of the testing framework, and that code from this
 # should not normally appear in tracebacks.
 __unittest = True

=== added file 'testtools/matchers/_datastructures.py'
--- testtools/matchers/_datastructures.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/_datastructures.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,228 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+__all__ = [
+    'ContainsAll',
+    'MatchesListwise',
+    'MatchesSetwise',
+    'MatchesStructure',
+    ]
+
+"""Matchers that operate with knowledge of Python data structures."""
+
+from ..helpers import map_values
+from ._core import Mismatch
+from ._higherorder import (
+    Annotate,
+    MatchesAll,
+    MismatchesAll,
+    )
+
+
+def ContainsAll(items):
+    """Make a matcher that checks whether a list of things is contained
+    in another thing.
+
+    The matcher effectively checks that the provided sequence is a subset of
+    the matchee.
+    """
+    from ._basic import Contains
+    return MatchesAll(*map(Contains, items), first_only=False)
+
+
+class MatchesListwise(object):
+    """Matches if each matcher matches the corresponding value.
+
+    More easily explained by example than in words:
+
+    >>> from ._basic import Equals
+    >>> MatchesListwise([Equals(1)]).match([1])
+    >>> MatchesListwise([Equals(1), Equals(2)]).match([1, 2])
+    >>> print (MatchesListwise([Equals(1), Equals(2)]).match([2, 1]).describe())
+    Differences: [
+    1 != 2
+    2 != 1
+    ]
+    >>> matcher = MatchesListwise([Equals(1), Equals(2)], first_only=True)
+    >>> print (matcher.match([3, 4]).describe())
+    1 != 3
+    """
+
+    def __init__(self, matchers, first_only=False):
+        """Construct a MatchesListwise matcher.
+
+        :param matchers: A list of matcher that the matched values must match.
+        :param first_only: If True, then only report the first mismatch,
+            otherwise report all of them. Defaults to False.
+        """
+        self.matchers = matchers
+        self.first_only = first_only
+
+    def match(self, values):
+        from ._basic import Equals
+        mismatches = []
+        length_mismatch = Annotate(
+            "Length mismatch", Equals(len(self.matchers))).match(len(values))
+        if length_mismatch:
+            mismatches.append(length_mismatch)
+        for matcher, value in zip(self.matchers, values):
+            mismatch = matcher.match(value)
+            if mismatch:
+                if self.first_only:
+                    return mismatch
+                mismatches.append(mismatch)
+        if mismatches:
+            return MismatchesAll(mismatches)
+
+
+class MatchesStructure(object):
+    """Matcher that matches an object structurally.
+
+    'Structurally' here means that attributes of the object being matched are
+    compared against given matchers.
+
+    `fromExample` allows the creation of a matcher from a prototype object and
+    then modified versions can be created with `update`.
+
+    `byEquality` creates a matcher in much the same way as the constructor,
+    except that the matcher for each of the attributes is assumed to be
+    `Equals`.
+
+    `byMatcher` creates a similar matcher to `byEquality`, but you get to pick
+    the matcher, rather than just using `Equals`.
+    """
+
+    def __init__(self, **kwargs):
+        """Construct a `MatchesStructure`.
+
+        :param kwargs: A mapping of attributes to matchers.
+        """
+        self.kws = kwargs
+
+    @classmethod
+    def byEquality(cls, **kwargs):
+        """Matches an object where the attributes equal the keyword values.
+
+        Similar to the constructor, except that the matcher is assumed to be
+        Equals.
+        """
+        from ._basic import Equals
+        return cls.byMatcher(Equals, **kwargs)
+
+    @classmethod
+    def byMatcher(cls, matcher, **kwargs):
+        """Matches an object where the attributes match the keyword values.
+
+        Similar to the constructor, except that the provided matcher is used
+        to match all of the values.
+        """
+        return cls(**map_values(matcher, kwargs))
+
+    @classmethod
+    def fromExample(cls, example, *attributes):
+        from ._basic import Equals
+        kwargs = {}
+        for attr in attributes:
+            kwargs[attr] = Equals(getattr(example, attr))
+        return cls(**kwargs)
+
+    def update(self, **kws):
+        new_kws = self.kws.copy()
+        for attr, matcher in kws.items():
+            if matcher is None:
+                new_kws.pop(attr, None)
+            else:
+                new_kws[attr] = matcher
+        return type(self)(**new_kws)
+
+    def __str__(self):
+        kws = []
+        for attr, matcher in sorted(self.kws.items()):
+            kws.append("%s=%s" % (attr, matcher))
+        return "%s(%s)" % (self.__class__.__name__, ', '.join(kws))
+
+    def match(self, value):
+        matchers = []
+        values = []
+        for attr, matcher in sorted(self.kws.items()):
+            matchers.append(Annotate(attr, matcher))
+            values.append(getattr(value, attr))
+        return MatchesListwise(matchers).match(values)
+
+
+class MatchesSetwise(object):
+    """Matches if all the matchers match elements of the value being matched.
+
+    That is, each element in the 'observed' set must match exactly one matcher
+    from the set of matchers, with no matchers left over.
+
+    The difference compared to `MatchesListwise` is that the order of the
+    matchings does not matter.
+    """
+
+    def __init__(self, *matchers):
+        self.matchers = matchers
+
+    def match(self, observed):
+        remaining_matchers = set(self.matchers)
+        not_matched = []
+        for value in observed:
+            for matcher in remaining_matchers:
+                if matcher.match(value) is None:
+                    remaining_matchers.remove(matcher)
+                    break
+            else:
+                not_matched.append(value)
+        if not_matched or remaining_matchers:
+            remaining_matchers = list(remaining_matchers)
+            # There are various cases that all should be reported somewhat
+            # differently.
+
+            # There are two trivial cases:
+            # 1) There are just some matchers left over.
+            # 2) There are just some values left over.
+
+            # Then there are three more interesting cases:
+            # 3) There are the same number of matchers and values left over.
+            # 4) There are more matchers left over than values.
+            # 5) There are more values left over than matchers.
+
+            if len(not_matched) == 0:
+                if len(remaining_matchers) > 1:
+                    msg = "There were %s matchers left over: " % (
+                        len(remaining_matchers),)
+                else:
+                    msg = "There was 1 matcher left over: "
+                msg += ', '.join(map(str, remaining_matchers))
+                return Mismatch(msg)
+            elif len(remaining_matchers) == 0:
+                if len(not_matched) > 1:
+                    return Mismatch(
+                        "There were %s values left over: %s" % (
+                            len(not_matched), not_matched))
+                else:
+                    return Mismatch(
+                        "There was 1 value left over: %s" % (
+                            not_matched, ))
+            else:
+                common_length = min(len(remaining_matchers), len(not_matched))
+                if common_length == 0:
+                    raise AssertionError("common_length can't be 0 here")
+                if common_length > 1:
+                    msg = "There were %s mismatches" % (common_length,)
+                else:
+                    msg = "There was 1 mismatch"
+                if len(remaining_matchers) > len(not_matched):
+                    extra_matchers = remaining_matchers[common_length:]
+                    msg += " and %s extra matcher" % (len(extra_matchers), )
+                    if len(extra_matchers) > 1:
+                        msg += "s"
+                    msg += ': ' + ', '.join(map(str, extra_matchers))
+                elif len(not_matched) > len(remaining_matchers):
+                    extra_values = not_matched[common_length:]
+                    msg += " and %s extra value" % (len(extra_values), )
+                    if len(extra_values) > 1:
+                        msg += "s"
+                    msg += ': ' + str(extra_values)
+                return Annotate(
+                    msg, MatchesListwise(remaining_matchers[:common_length])
+                    ).match(not_matched[:common_length])

=== added file 'testtools/matchers/_dict.py'
--- testtools/matchers/_dict.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/_dict.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,260 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+__all__ = [
+    'KeysEqual',
+    ]
+
+from ..helpers import (
+    dict_subtract,
+    filter_values,
+    map_values,
+    )
+from ._core import Matcher, Mismatch
+from ._higherorder import (
+    AnnotatedMismatch,
+    PrefixedMismatch,
+    MismatchesAll,
+    )
+
+
+def LabelledMismatches(mismatches, details=None):
+    """A collection of mismatches, each labelled."""
+    return MismatchesAll(
+        (PrefixedMismatch(k, v) for (k, v) in sorted(mismatches.items())),
+        wrap=False)
+
+
+class MatchesAllDict(Matcher):
+    """Matches if all of the matchers it is created with match.
+
+    A lot like ``MatchesAll``, but takes a dict of Matchers and labels any
+    mismatches with the key of the dictionary.
+    """
+
+    def __init__(self, matchers):
+        super(MatchesAllDict, self).__init__()
+        self.matchers = matchers
+
+    def __str__(self):
+        return 'MatchesAllDict({%s})' % (
+            ', '.join('%r: %s' % (k, v) for k, v in self.matchers.items()))
+
+    def match(self, observed):
+        mismatches = {}
+        for label in self.matchers:
+            mismatches[label] = self.matchers[label].match(observed)
+        return _dict_to_mismatch(
+            mismatches, result_mismatch=LabelledMismatches)
+
+
+class DictMismatches(Mismatch):
+    """A mismatch with a dict of child mismatches."""
+
+    def __init__(self, mismatches, details=None):
+        super(DictMismatches, self).__init__(None, details=details)
+        self.mismatches = mismatches
+
+    def describe(self):
+        lines = ['{']
+        lines.extend(
+            ['  %r: %s,' % (key, mismatch.describe())
+             for (key, mismatch) in sorted(self.mismatches.items())])
+        lines.append('}')
+        return '\n'.join(lines)
+
+
+def _dict_to_mismatch(data, to_mismatch=None,
+                      result_mismatch=DictMismatches):
+    if to_mismatch:
+        data = map_values(to_mismatch, data)
+    mismatches = filter_values(bool, data)
+    if mismatches:
+        return result_mismatch(mismatches)
+
+
+class _MatchCommonKeys(Matcher):
+    """Match on keys in a dictionary.
+
+    Given a dictionary where the values are matchers, this will look for
+    common keys in the matched dictionary and match if and only if all common
+    keys match the given matchers.
+
+    Thus::
+
+      >>> structure = {'a': Equals('x'), 'b': Equals('y')}
+      >>> _MatchCommonKeys(structure).match({'a': 'x', 'c': 'z'})
+      None
+    """
+
+    def __init__(self, dict_of_matchers):
+        super(_MatchCommonKeys, self).__init__()
+        self._matchers = dict_of_matchers
+
+    def _compare_dicts(self, expected, observed):
+        common_keys = set(expected.keys()) & set(observed.keys())
+        mismatches = {}
+        for key in common_keys:
+            mismatch = expected[key].match(observed[key])
+            if mismatch:
+                mismatches[key] = mismatch
+        return mismatches
+
+    def match(self, observed):
+        mismatches = self._compare_dicts(self._matchers, observed)
+        if mismatches:
+            return DictMismatches(mismatches)
+
+
+class _SubDictOf(Matcher):
+    """Matches if the matched dict only has keys that are in given dict."""
+
+    def __init__(self, super_dict, format_value=repr):
+        super(_SubDictOf, self).__init__()
+        self.super_dict = super_dict
+        self.format_value = format_value
+
+    def match(self, observed):
+        excess = dict_subtract(observed, self.super_dict)
+        return _dict_to_mismatch(
+            excess, lambda v: Mismatch(self.format_value(v)))
+
+
+class _SuperDictOf(Matcher):
+    """Matches if all of the keys in the given dict are in the matched dict.
+    """
+
+    def __init__(self, sub_dict, format_value=repr):
+        super(_SuperDictOf, self).__init__()
+        self.sub_dict = sub_dict
+        self.format_value = format_value
+
+    def match(self, super_dict):
+        return _SubDictOf(super_dict, self.format_value).match(self.sub_dict)
+
+
+def _format_matcher_dict(matchers):
+    return '{%s}' % (
+        ', '.join('%r: %s' % (k, v) for k, v in matchers.items()))
+
+
+class _CombinedMatcher(Matcher):
+    """Many matchers labelled and combined into one uber-matcher.
+
+    Subclass this and then specify a dict of matcher factories that take a
+    single 'expected' value and return a matcher.  The subclass will match
+    only if all of the matchers made from factories match.
+
+    Not **entirely** dissimilar from ``MatchesAll``.
+    """
+
+    matcher_factories = {}
+
+    def __init__(self, expected):
+        super(_CombinedMatcher, self).__init__()
+        self._expected = expected
+
+    def format_expected(self, expected):
+        return repr(expected)
+
+    def __str__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__, self.format_expected(self._expected))
+
+    def match(self, observed):
+        matchers = dict(
+            (k, v(self._expected)) for k, v in self.matcher_factories.items())
+        return MatchesAllDict(matchers).match(observed)
+
+
+class MatchesDict(_CombinedMatcher):
+    """Match a dictionary exactly, by its keys.
+
+    Specify a dictionary mapping keys (often strings) to matchers.  This is
+    the 'expected' dict.  Any dictionary that matches this must have exactly
+    the same keys, and the values must match the corresponding matchers in the
+    expected dict.
+    """
+
+    matcher_factories = {
+        'Extra': _SubDictOf,
+        'Missing': lambda m: _SuperDictOf(m, format_value=str),
+        'Differences': _MatchCommonKeys,
+        }
+
+    format_expected = lambda self, expected: _format_matcher_dict(expected)
+
+
+class ContainsDict(_CombinedMatcher):
+    """Match a dictionary for that contains a specified sub-dictionary.
+
+    Specify a dictionary mapping keys (often strings) to matchers.  This is
+    the 'expected' dict.  Any dictionary that matches this must have **at
+    least** these keys, and the values must match the corresponding matchers
+    in the expected dict.  Dictionaries that have more keys will also match.
+
+    In other words, any matching dictionary must contain the dictionary given
+    to the constructor.
+
+    Does not check for strict sub-dictionary.  That is, equal dictionaries
+    match.
+    """
+
+    matcher_factories = {
+        'Missing': lambda m: _SuperDictOf(m, format_value=str),
+        'Differences': _MatchCommonKeys,
+        }
+
+    format_expected = lambda self, expected: _format_matcher_dict(expected)
+
+
+class ContainedByDict(_CombinedMatcher):
+    """Match a dictionary for which this is a super-dictionary.
+
+    Specify a dictionary mapping keys (often strings) to matchers.  This is
+    the 'expected' dict.  Any dictionary that matches this must have **only**
+    these keys, and the values must match the corresponding matchers in the
+    expected dict.  Dictionaries that have fewer keys can also match.
+
+    In other words, any matching dictionary must be contained by the
+    dictionary given to the constructor.
+
+    Does not check for strict super-dictionary.  That is, equal dictionaries
+    match.
+    """
+
+    matcher_factories = {
+        'Extra': _SubDictOf,
+        'Differences': _MatchCommonKeys,
+        }
+
+    format_expected = lambda self, expected: _format_matcher_dict(expected)
+
+
+class KeysEqual(Matcher):
+    """Checks whether a dict has particular keys."""
+
+    def __init__(self, *expected):
+        """Create a `KeysEqual` Matcher.
+
+        :param expected: The keys the dict is expected to have.  If a dict,
+            then we use the keys of that dict, if a collection, we assume it
+            is a collection of expected keys.
+        """
+        super(KeysEqual, self).__init__()
+        try:
+            self.expected = expected.keys()
+        except AttributeError:
+            self.expected = list(expected)
+
+    def __str__(self):
+        return "KeysEqual(%s)" % ', '.join(map(repr, self.expected))
+
+    def match(self, matchee):
+        from ._basic import _BinaryMismatch, Equals
+        expected = sorted(self.expected)
+        matched = Equals(expected).match(sorted(matchee.keys()))
+        if matched:
+            return AnnotatedMismatch(
+                'Keys not equal',
+                _BinaryMismatch(expected, 'does not match', matchee))
+        return None

=== added file 'testtools/matchers/_doctest.py'
--- testtools/matchers/_doctest.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/_doctest.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,104 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+__all__ = [
+    'DocTestMatches',
+    ]
+
+import doctest
+import re
+
+from ..compat import str_is_unicode
+from ._core import Mismatch
+
+
+class _NonManglingOutputChecker(doctest.OutputChecker):
+    """Doctest checker that works with unicode rather than mangling strings
+
+    This is needed because current Python versions have tried to fix string
+    encoding related problems, but regressed the default behaviour with
+    unicode inputs in the process.
+
+    In Python 2.6 and 2.7 ``OutputChecker.output_difference`` is was changed
+    to return a bytestring encoded as per ``sys.stdout.encoding``, or utf-8 if
+    that can't be determined. Worse, that encoding process happens in the
+    innocent looking `_indent` global function. Because the
+    `DocTestMismatch.describe` result may well not be destined for printing to
+    stdout, this is no good for us. To get a unicode return as before, the
+    method is monkey patched if ``doctest._encoding`` exists.
+
+    Python 3 has a different problem. For some reason both inputs are encoded
+    to ascii with 'backslashreplace', making an escaped string matches its
+    unescaped form. Overriding the offending ``OutputChecker._toAscii`` method
+    is sufficient to revert this.
+    """
+
+    def _toAscii(self, s):
+        """Return ``s`` unchanged rather than mangling it to ascii"""
+        return s
+
+    # Only do this overriding hackery if doctest has a broken _input function
+    if getattr(doctest, "_encoding", None) is not None:
+        from types import FunctionType as __F
+        __f = doctest.OutputChecker.output_difference.im_func
+        __g = dict(__f.func_globals)
+        def _indent(s, indent=4, _pattern=re.compile("^(?!$)", re.MULTILINE)):
+            """Prepend non-empty lines in ``s`` with ``indent`` number of spaces"""
+            return _pattern.sub(indent*" ", s)
+        __g["_indent"] = _indent
+        output_difference = __F(__f.func_code, __g, "output_difference")
+        del __F, __f, __g, _indent
+
+
+class DocTestMatches(object):
+    """See if a string matches a doctest example."""
+
+    def __init__(self, example, flags=0):
+        """Create a DocTestMatches to match example.
+
+        :param example: The example to match e.g. 'foo bar baz'
+        :param flags: doctest comparison flags to match on. e.g.
+            doctest.ELLIPSIS.
+        """
+        if not example.endswith('\n'):
+            example += '\n'
+        self.want = example # required variable name by doctest.
+        self.flags = flags
+        self._checker = _NonManglingOutputChecker()
+
+    def __str__(self):
+        if self.flags:
+            flagstr = ", flags=%d" % self.flags
+        else:
+            flagstr = ""
+        return 'DocTestMatches(%r%s)' % (self.want, flagstr)
+
+    def _with_nl(self, actual):
+        result = self.want.__class__(actual)
+        if not result.endswith('\n'):
+            result += '\n'
+        return result
+
+    def match(self, actual):
+        with_nl = self._with_nl(actual)
+        if self._checker.check_output(self.want, with_nl, self.flags):
+            return None
+        return DocTestMismatch(self, with_nl)
+
+    def _describe_difference(self, with_nl):
+        return self._checker.output_difference(self, with_nl, self.flags)
+
+
+class DocTestMismatch(Mismatch):
+    """Mismatch object for DocTestMatches."""
+
+    def __init__(self, matcher, with_nl):
+        self.matcher = matcher
+        self.with_nl = with_nl
+
+    def describe(self):
+        s = self.matcher._describe_difference(self.with_nl)
+        if str_is_unicode or isinstance(s, unicode):
+            return s
+        # GZ 2011-08-24: This is actually pretty bogus, most C0 codes should
+        #                be escaped, in addition to non-ascii bytes.
+        return s.decode("latin1").encode("ascii", "backslashreplace")

=== added file 'testtools/matchers/_exception.py'
--- testtools/matchers/_exception.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/_exception.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,124 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+__all__ = [
+    'MatchesException',
+    'Raises',
+    'raises',
+    ]
+
+import sys
+
+from testtools.compat import (
+    classtypes,
+    _error_repr,
+    isbaseexception,
+    istext,
+    )
+from ._basic import MatchesRegex
+from ._core import (
+    Matcher,
+    Mismatch,
+    )
+from ._higherorder import AfterPreproccessing
+
+
+class MatchesException(Matcher):
+    """Match an exc_info tuple against an exception instance or type."""
+
+    def __init__(self, exception, value_re=None):
+        """Create a MatchesException that will match exc_info's for exception.
+
+        :param exception: Either an exception instance or type.
+            If an instance is given, the type and arguments of the exception
+            are checked. If a type is given only the type of the exception is
+            checked. If a tuple is given, then as with isinstance, any of the
+            types in the tuple matching is sufficient to match.
+        :param value_re: If 'exception' is a type, and the matchee exception
+            is of the right type, then match against this.  If value_re is a
+            string, then assume value_re is a regular expression and match
+            the str() of the exception against it.  Otherwise, assume value_re
+            is a matcher, and match the exception against it.
+        """
+        Matcher.__init__(self)
+        self.expected = exception
+        if istext(value_re):
+            value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
+        self.value_re = value_re
+        self._is_instance = type(self.expected) not in classtypes() + (tuple,)
+
+    def match(self, other):
+        if type(other) != tuple:
+            return Mismatch('%r is not an exc_info tuple' % other)
+        expected_class = self.expected
+        if self._is_instance:
+            expected_class = expected_class.__class__
+        if not issubclass(other[0], expected_class):
+            return Mismatch('%r is not a %r' % (other[0], expected_class))
+        if self._is_instance:
+            if other[1].args != self.expected.args:
+                return Mismatch('%s has different arguments to %s.' % (
+                        _error_repr(other[1]), _error_repr(self.expected)))
+        elif self.value_re is not None:
+            return self.value_re.match(other[1])
+
+    def __str__(self):
+        if self._is_instance:
+            return "MatchesException(%s)" % _error_repr(self.expected)
+        return "MatchesException(%s)" % repr(self.expected)
+
+
+class Raises(Matcher):
+    """Match if the matchee raises an exception when called.
+
+    Exceptions which are not subclasses of Exception propogate out of the
+    Raises.match call unless they are explicitly matched.
+    """
+
+    def __init__(self, exception_matcher=None):
+        """Create a Raises matcher.
+
+        :param exception_matcher: Optional validator for the exception raised
+            by matchee. If supplied the exc_info tuple for the exception raised
+            is passed into that matcher. If no exception_matcher is supplied
+            then the simple fact of raising an exception is considered enough
+            to match on.
+        """
+        self.exception_matcher = exception_matcher
+
+    def match(self, matchee):
+        try:
+            result = matchee()
+            return Mismatch('%r returned %r' % (matchee, result))
+        # Catch all exceptions: Raises() should be able to match a
+        # KeyboardInterrupt or SystemExit.
+        except:
+            exc_info = sys.exc_info()
+            if self.exception_matcher:
+                mismatch = self.exception_matcher.match(exc_info)
+                if not mismatch:
+                    del exc_info
+                    return
+            else:
+                mismatch = None
+            # The exception did not match, or no explicit matching logic was
+            # performed. If the exception is a non-user exception (that is, not
+            # a subclass of Exception on Python 2.5+) then propogate it.
+            if isbaseexception(exc_info[1]):
+                del exc_info
+                raise
+            return mismatch
+
+    def __str__(self):
+        return 'Raises()'
+
+
+def raises(exception):
+    """Make a matcher that checks that a callable raises an exception.
+
+    This is a convenience function, exactly equivalent to::
+
+        return Raises(MatchesException(exception))
+
+    See `Raises` and `MatchesException` for more information.
+    """
+    return Raises(MatchesException(exception))

=== added file 'testtools/matchers/_filesystem.py'
--- testtools/matchers/_filesystem.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/_filesystem.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,192 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+"""Matchers for things related to the filesystem."""
+
+__all__ = [
+    'FileContains',
+    'DirExists',
+    'FileExists',
+    'HasPermissions',
+    'PathExists',
+    'SamePath',
+    'TarballContains',
+    ]
+
+import os
+import tarfile
+
+from ._basic import Equals
+from ._core import (
+    Matcher,
+    )
+from ._higherorder import (
+    MatchesAll,
+    MatchesPredicate,
+    )
+
+
+def PathExists():
+    """Matches if the given path exists.
+
+    Use like this::
+
+      assertThat('/some/path', PathExists())
+    """
+    return MatchesPredicate(os.path.exists, "%s does not exist.")
+
+
+def DirExists():
+    """Matches if the path exists and is a directory."""
+    return MatchesAll(
+        PathExists(),
+        MatchesPredicate(os.path.isdir, "%s is not a directory."),
+        first_only=True)
+
+
+def FileExists():
+    """Matches if the given path exists and is a file."""
+    return MatchesAll(
+        PathExists(),
+        MatchesPredicate(os.path.isfile, "%s is not a file."),
+        first_only=True)
+
+
+class DirContains(Matcher):
+    """Matches if the given directory contains files with the given names.
+
+    That is, is the directory listing exactly equal to the given files?
+    """
+
+    def __init__(self, filenames=None, matcher=None):
+        """Construct a ``DirContains`` matcher.
+
+        Can be used in a basic mode where the whole directory listing is
+        matched against an expected directory listing (by passing
+        ``filenames``).  Can also be used in a more advanced way where the
+        whole directory listing is matched against an arbitrary matcher (by
+        passing ``matcher`` instead).
+
+        :param filenames: If specified, match the sorted directory listing
+            against this list of filenames, sorted.
+        :param matcher: If specified, match the sorted directory listing
+            against this matcher.
+        """
+        if filenames == matcher == None:
+            raise AssertionError(
+                "Must provide one of `filenames` or `matcher`.")
+        if None not in (filenames, matcher):
+            raise AssertionError(
+                "Must provide either `filenames` or `matcher`, not both.")
+        if filenames is None:
+            self.matcher = matcher
+        else:
+            self.matcher = Equals(sorted(filenames))
+
+    def match(self, path):
+        mismatch = DirExists().match(path)
+        if mismatch is not None:
+            return mismatch
+        return self.matcher.match(sorted(os.listdir(path)))
+
+
+class FileContains(Matcher):
+    """Matches if the given file has the specified contents."""
+
+    def __init__(self, contents=None, matcher=None):
+        """Construct a ``FileContains`` matcher.
+
+        Can be used in a basic mode where the file contents are compared for
+        equality against the expected file contents (by passing ``contents``).
+        Can also be used in a more advanced way where the file contents are
+        matched against an arbitrary matcher (by passing ``matcher`` instead).
+
+        :param contents: If specified, match the contents of the file with
+            these contents.
+        :param matcher: If specified, match the contents of the file against
+            this matcher.
+        """
+        if contents == matcher == None:
+            raise AssertionError(
+                "Must provide one of `contents` or `matcher`.")
+        if None not in (contents, matcher):
+            raise AssertionError(
+                "Must provide either `contents` or `matcher`, not both.")
+        if matcher is None:
+            self.matcher = Equals(contents)
+        else:
+            self.matcher = matcher
+
+    def match(self, path):
+        mismatch = PathExists().match(path)
+        if mismatch is not None:
+            return mismatch
+        f = open(path)
+        try:
+            actual_contents = f.read()
+            return self.matcher.match(actual_contents)
+        finally:
+            f.close()
+
+    def __str__(self):
+        return "File at path exists and contains %s" % self.contents
+
+
+class HasPermissions(Matcher):
+    """Matches if a file has the given permissions.
+
+    Permissions are specified and matched as a four-digit octal string.
+    """
+
+    def __init__(self, octal_permissions):
+        """Construct a HasPermissions matcher.
+
+        :param octal_permissions: A four digit octal string, representing the
+            intended access permissions. e.g. '0775' for rwxrwxr-x.
+        """
+        super(HasPermissions, self).__init__()
+        self.octal_permissions = octal_permissions
+
+    def match(self, filename):
+        permissions = oct(os.stat(filename).st_mode)[-4:]
+        return Equals(self.octal_permissions).match(permissions)
+
+
+class SamePath(Matcher):
+    """Matches if two paths are the same.
+
+    That is, the paths are equal, or they point to the same file but in
+    different ways.  The paths do not have to exist.
+    """
+
+    def __init__(self, path):
+        super(SamePath, self).__init__()
+        self.path = path
+
+    def match(self, other_path):
+        f = lambda x: os.path.abspath(os.path.realpath(x))
+        return Equals(f(self.path)).match(f(other_path))
+
+
+class TarballContains(Matcher):
+    """Matches if the given tarball contains the given paths.
+
+    Uses TarFile.getnames() to get the paths out of the tarball.
+    """
+
+    def __init__(self, paths):
+        super(TarballContains, self).__init__()
+        self.paths = paths
+        self.path_matcher = Equals(sorted(self.paths))
+
+    def match(self, tarball_path):
+        # Open underlying file first to ensure it's always closed:
+        # <http://bugs.python.org/issue10233>
+        f = open(tarball_path, "rb")
+        try:
+            tarball = tarfile.open(tarball_path, fileobj=f)
+            try:
+                return self.path_matcher.match(sorted(tarball.getnames()))
+            finally:
+                tarball.close()
+        finally:
+            f.close()

=== added file 'testtools/matchers/_higherorder.py'
--- testtools/matchers/_higherorder.py	1970-01-01 00:00:00 +0000
+++ testtools/matchers/_higherorder.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,269 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+__all__ = [
+    'AfterPreprocessing',
+    'AllMatch',
+    'Annotate',
+    'MatchesAny',
+    'MatchesAll',
+    'Not',
+    ]
+
+import types
+
+from ._core import (
+    Matcher,
+    Mismatch,
+    MismatchDecorator,
+    )
+
+
+class MatchesAny(object):
+    """Matches if any of the matchers it is created with match."""
+
+    def __init__(self, *matchers):
+        self.matchers = matchers
+
+    def match(self, matchee):
+        results = []
+        for matcher in self.matchers:
+            mismatch = matcher.match(matchee)
+            if mismatch is None:
+                return None
+            results.append(mismatch)
+        return MismatchesAll(results)
+
+    def __str__(self):
+        return "MatchesAny(%s)" % ', '.join([
+            str(matcher) for matcher in self.matchers])
+
+
+class MatchesAll(object):
+    """Matches if all of the matchers it is created with match."""
+
+    def __init__(self, *matchers, **options):
+        """Construct a MatchesAll matcher.
+
+        Just list the component matchers as arguments in the ``*args``
+        style. If you want only the first mismatch to be reported, past in
+        first_only=True as a keyword argument. By default, all mismatches are
+        reported.
+        """
+        self.matchers = matchers
+        self.first_only = options.get('first_only', False)
+
+    def __str__(self):
+        return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers))
+
+    def match(self, matchee):
+        results = []
+        for matcher in self.matchers:
+            mismatch = matcher.match(matchee)
+            if mismatch is not None:
+                if self.first_only:
+                    return mismatch
+                results.append(mismatch)
+        if results:
+            return MismatchesAll(results)
+        else:
+            return None
+
+
+class MismatchesAll(Mismatch):
+    """A mismatch with many child mismatches."""
+
+    def __init__(self, mismatches, wrap=True):
+        self.mismatches = mismatches
+        self._wrap = wrap
+
+    def describe(self):
+        descriptions = []
+        if self._wrap:
+            descriptions = ["Differences: ["]
+        for mismatch in self.mismatches:
+            descriptions.append(mismatch.describe())
+        if self._wrap:
+            descriptions.append("]")
+        return '\n'.join(descriptions)
+
+
+class Not(object):
+    """Inverts a matcher."""
+
+    def __init__(self, matcher):
+        self.matcher = matcher
+
+    def __str__(self):
+        return 'Not(%s)' % (self.matcher,)
+
+    def match(self, other):
+        mismatch = self.matcher.match(other)
+        if mismatch is None:
+            return MatchedUnexpectedly(self.matcher, other)
+        else:
+            return None
+
+
+class MatchedUnexpectedly(Mismatch):
+    """A thing matched when it wasn't supposed to."""
+
+    def __init__(self, matcher, other):
+        self.matcher = matcher
+        self.other = other
+
+    def describe(self):
+        return "%r matches %s" % (self.other, self.matcher)
+
+
+class Annotate(object):
+    """Annotates a matcher with a descriptive string.
+
+    Mismatches are then described as '<mismatch>: <annotation>'.
+    """
+
+    def __init__(self, annotation, matcher):
+        self.annotation = annotation
+        self.matcher = matcher
+
+    @classmethod
+    def if_message(cls, annotation, matcher):
+        """Annotate ``matcher`` only if ``annotation`` is non-empty."""
+        if not annotation:
+            return matcher
+        return cls(annotation, matcher)
+
+    def __str__(self):
+        return 'Annotate(%r, %s)' % (self.annotation, self.matcher)
+
+    def match(self, other):
+        mismatch = self.matcher.match(other)
+        if mismatch is not None:
+            return AnnotatedMismatch(self.annotation, mismatch)
+
+
+class PostfixedMismatch(MismatchDecorator):
+    """A mismatch annotated with a descriptive string."""
+
+    def __init__(self, annotation, mismatch):
+        super(PostfixedMismatch, self).__init__(mismatch)
+        self.annotation = annotation
+        self.mismatch = mismatch
+
+    def describe(self):
+        return '%s: %s' % (self.original.describe(), self.annotation)
+
+
+AnnotatedMismatch = PostfixedMismatch
+
+
+class PrefixedMismatch(MismatchDecorator):
+
+    def __init__(self, prefix, mismatch):
+        super(PrefixedMismatch, self).__init__(mismatch)
+        self.prefix = prefix
+
+    def describe(self):
+        return '%s: %s' % (self.prefix, self.original.describe())
+
+
+class AfterPreprocessing(object):
+    """Matches if the value matches after passing through a function.
+
+    This can be used to aid in creating trivial matchers as functions, for
+    example::
+
+      def PathHasFileContent(content):
+          def _read(path):
+              return open(path).read()
+          return AfterPreprocessing(_read, Equals(content))
+    """
+
+    def __init__(self, preprocessor, matcher, annotate=True):
+        """Create an AfterPreprocessing matcher.
+
+        :param preprocessor: A function called with the matchee before
+            matching.
+        :param matcher: What to match the preprocessed matchee against.
+        :param annotate: Whether or not to annotate the matcher with
+            something explaining how we transformed the matchee. Defaults
+            to True.
+        """
+        self.preprocessor = preprocessor
+        self.matcher = matcher
+        self.annotate = annotate
+
+    def _str_preprocessor(self):
+        if isinstance(self.preprocessor, types.FunctionType):
+            return '<function %s>' % self.preprocessor.__name__
+        return str(self.preprocessor)
+
+    def __str__(self):
+        return "AfterPreprocessing(%s, %s)" % (
+            self._str_preprocessor(), self.matcher)
+
+    def match(self, value):
+        after = self.preprocessor(value)
+        if self.annotate:
+            matcher = Annotate(
+                "after %s on %r" % (self._str_preprocessor(), value),
+                self.matcher)
+        else:
+            matcher = self.matcher
+        return matcher.match(after)
+
+
+# This is the old, deprecated. spelling of the name, kept for backwards
+# compatibility.
+AfterPreproccessing = AfterPreprocessing
+
+
+class AllMatch(object):
+    """Matches if all provided values match the given matcher."""
+
+    def __init__(self, matcher):
+        self.matcher = matcher
+
+    def __str__(self):
+        return 'AllMatch(%s)' % (self.matcher,)
+
+    def match(self, values):
+        mismatches = []
+        for value in values:
+            mismatch = self.matcher.match(value)
+            if mismatch:
+                mismatches.append(mismatch)
+        if mismatches:
+            return MismatchesAll(mismatches)
+
+
+class MatchesPredicate(Matcher):
+    """Match if a given function returns True.
+
+    It is reasonably common to want to make a very simple matcher based on a
+    function that you already have that returns True or False given a single
+    argument (i.e. a predicate function).  This matcher makes it very easy to
+    do so. e.g.::
+
+      IsEven = MatchesPredicate(lambda x: x % 2 == 0, '%s is not even')
+      self.assertThat(4, IsEven)
+    """
+
+    def __init__(self, predicate, message):
+        """Create a ``MatchesPredicate`` matcher.
+
+        :param predicate: A function that takes a single argument and returns
+            a value that will be interpreted as a boolean.
+        :param message: A message to describe a mismatch.  It will be formatted
+            with '%' and be given whatever was passed to ``match()``. Thus, it
+            needs to contain exactly one thing like '%s', '%d' or '%f'.
+        """
+        self.predicate = predicate
+        self.message = message
+
+    def __str__(self):
+        return '%s(%r, %r)' % (
+            self.__class__.__name__, self.predicate, self.message)
+
+    def match(self, x):
+        if not self.predicate(x):
+            return Mismatch(self.message % x)

=== modified file 'testtools/tests/__init__.py'
--- testtools/tests/__init__.py	2012-04-11 11:00:52 +0000
+++ testtools/tests/__init__.py	2012-09-08 17:30:27 +0000
@@ -7,6 +7,7 @@
 
 def test_suite():
     from testtools.tests import (
+        matchers,
         test_compat,
         test_content,
         test_content_type,
@@ -14,7 +15,6 @@
         test_distutilscmd,
         test_fixturesupport,
         test_helpers,
-        test_matchers,
         test_monkey,
         test_run,
         test_runtest,
@@ -25,6 +25,7 @@
         test_testsuite,
         )
     modules = [
+        matchers,
         test_compat,
         test_content,
         test_content_type,
@@ -32,7 +33,6 @@
         test_distutilscmd,
         test_fixturesupport,
         test_helpers,
-        test_matchers,
         test_monkey,
         test_run,
         test_runtest,

=== added directory 'testtools/tests/matchers'
=== added file 'testtools/tests/matchers/__init__.py'
--- testtools/tests/matchers/__init__.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/__init__.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,29 @@
+# Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
+
+
+from unittest import TestSuite
+
+
+def test_suite():
+    from testtools.tests.matchers import (
+        test_basic,
+        test_core,
+        test_datastructures,
+        test_dict,
+        test_doctest,
+        test_exception,
+        test_filesystem,
+        test_higherorder,
+        )
+    modules = [
+        test_basic,
+        test_core,
+        test_datastructures,
+        test_dict,
+        test_doctest,
+        test_exception,
+        test_filesystem,
+        test_higherorder,
+        ]
+    suites = map(lambda x: x.test_suite(), modules)
+    return TestSuite(suites)

=== added file 'testtools/tests/matchers/helpers.py'
--- testtools/tests/matchers/helpers.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/helpers.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,42 @@
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
+
+from testtools.tests.helpers import FullStackRunTest
+
+
+class TestMatchersInterface(object):
+
+    run_tests_with = FullStackRunTest
+
+    def test_matches_match(self):
+        matcher = self.matches_matcher
+        matches = self.matches_matches
+        mismatches = self.matches_mismatches
+        for candidate in matches:
+            self.assertEqual(None, matcher.match(candidate))
+        for candidate in mismatches:
+            mismatch = matcher.match(candidate)
+            self.assertNotEqual(None, mismatch)
+            self.assertNotEqual(None, getattr(mismatch, 'describe', None))
+
+    def test__str__(self):
+        # [(expected, object to __str__)].
+        from testtools.matchers._doctest import DocTestMatches
+        examples = self.str_examples
+        for expected, matcher in examples:
+            self.assertThat(matcher, DocTestMatches(expected))
+
+    def test_describe_difference(self):
+        # [(expected, matchee, matcher), ...]
+        examples = self.describe_examples
+        for difference, matchee, matcher in examples:
+            mismatch = matcher.match(matchee)
+            self.assertEqual(difference, mismatch.describe())
+
+    def test_mismatch_details(self):
+        # The mismatch object must provide get_details, which must return a
+        # dictionary mapping names to Content objects.
+        examples = self.describe_examples
+        for difference, matchee, matcher in examples:
+            mismatch = matcher.match(matchee)
+            details = mismatch.get_details()
+            self.assertEqual(dict(details), details)

=== added file 'testtools/tests/matchers/test_basic.py'
--- testtools/tests/matchers/test_basic.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/test_basic.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,374 @@
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
+
+import re
+
+from testtools import TestCase
+from testtools.compat import (
+    text_repr,
+    _b,
+    _u,
+    )
+from testtools.matchers._basic import (
+    _BinaryMismatch,
+    Contains,
+    DoesNotEndWith,
+    DoesNotStartWith,
+    EndsWith,
+    Equals,
+    Is,
+    IsInstance,
+    LessThan,
+    GreaterThan,
+    MatchesRegex,
+    NotEquals,
+    SameMembers,
+    StartsWith,
+    )
+from testtools.tests.helpers import FullStackRunTest
+from testtools.tests.matchers.helpers import TestMatchersInterface
+
+
+class Test_BinaryMismatch(TestCase):
+    """Mismatches from binary comparisons need useful describe output"""
+
+    _long_string = "This is a longish multiline non-ascii string\n\xa7"
+    _long_b = _b(_long_string)
+    _long_u = _u(_long_string)
+
+    def test_short_objects(self):
+        o1, o2 = object(), object()
+        mismatch = _BinaryMismatch(o1, "!~", o2)
+        self.assertEqual(mismatch.describe(), "%r !~ %r" % (o1, o2))
+
+    def test_short_mixed_strings(self):
+        b, u = _b("\xa7"), _u("\xa7")
+        mismatch = _BinaryMismatch(b, "!~", u)
+        self.assertEqual(mismatch.describe(), "%r !~ %r" % (b, u))
+
+    def test_long_bytes(self):
+        one_line_b = self._long_b.replace(_b("\n"), _b(" "))
+        mismatch = _BinaryMismatch(one_line_b, "!~", self._long_b)
+        self.assertEqual(mismatch.describe(),
+            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
+                text_repr(one_line_b),
+                text_repr(self._long_b, multiline=True)))
+
+    def test_long_unicode(self):
+        one_line_u = self._long_u.replace("\n", " ")
+        mismatch = _BinaryMismatch(one_line_u, "!~", self._long_u)
+        self.assertEqual(mismatch.describe(),
+            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
+                text_repr(one_line_u),
+                text_repr(self._long_u, multiline=True)))
+
+    def test_long_mixed_strings(self):
+        mismatch = _BinaryMismatch(self._long_b, "!~", self._long_u)
+        self.assertEqual(mismatch.describe(),
+            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
+                text_repr(self._long_b, multiline=True),
+                text_repr(self._long_u, multiline=True)))
+
+    def test_long_bytes_and_object(self):
+        obj = object()
+        mismatch = _BinaryMismatch(self._long_b, "!~", obj)
+        self.assertEqual(mismatch.describe(),
+            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
+                text_repr(self._long_b, multiline=True),
+                repr(obj)))
+
+    def test_long_unicode_and_object(self):
+        obj = object()
+        mismatch = _BinaryMismatch(self._long_u, "!~", obj)
+        self.assertEqual(mismatch.describe(),
+            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
+                text_repr(self._long_u, multiline=True),
+                repr(obj)))
+
+
+class TestEqualsInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = Equals(1)
+    matches_matches = [1]
+    matches_mismatches = [2]
+
+    str_examples = [("Equals(1)", Equals(1)), ("Equals('1')", Equals('1'))]
+
+    describe_examples = [("1 != 2", 2, Equals(1))]
+
+
+class TestNotEqualsInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = NotEquals(1)
+    matches_matches = [2]
+    matches_mismatches = [1]
+
+    str_examples = [
+        ("NotEquals(1)", NotEquals(1)), ("NotEquals('1')", NotEquals('1'))]
+
+    describe_examples = [("1 == 1", 1, NotEquals(1))]
+
+
+class TestIsInterface(TestCase, TestMatchersInterface):
+
+    foo = object()
+    bar = object()
+
+    matches_matcher = Is(foo)
+    matches_matches = [foo]
+    matches_mismatches = [bar, 1]
+
+    str_examples = [("Is(2)", Is(2))]
+
+    describe_examples = [("1 is not 2", 2, Is(1))]
+
+
+class TestIsInstanceInterface(TestCase, TestMatchersInterface):
+
+    class Foo:pass
+
+    matches_matcher = IsInstance(Foo)
+    matches_matches = [Foo()]
+    matches_mismatches = [object(), 1, Foo]
+
+    str_examples = [
+            ("IsInstance(str)", IsInstance(str)),
+            ("IsInstance(str, int)", IsInstance(str, int)),
+            ]
+
+    describe_examples = [
+            ("'foo' is not an instance of int", 'foo', IsInstance(int)),
+            ("'foo' is not an instance of any of (int, type)", 'foo',
+             IsInstance(int, type)),
+            ]
+
+
+class TestLessThanInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = LessThan(4)
+    matches_matches = [-5, 3]
+    matches_mismatches = [4, 5, 5000]
+
+    str_examples = [
+        ("LessThan(12)", LessThan(12)),
+        ]
+
+    describe_examples = [
+        ('4 is not > 5', 5, LessThan(4)),
+        ('4 is not > 4', 4, LessThan(4)),
+        ]
+
+
+class TestGreaterThanInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = GreaterThan(4)
+    matches_matches = [5, 8]
+    matches_mismatches = [-2, 0, 4]
+
+    str_examples = [
+        ("GreaterThan(12)", GreaterThan(12)),
+        ]
+
+    describe_examples = [
+        ('5 is not < 4', 4, GreaterThan(5)),
+        ('4 is not < 4', 4, GreaterThan(4)),
+        ]
+
+
+class TestContainsInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = Contains('foo')
+    matches_matches = ['foo', 'afoo', 'fooa']
+    matches_mismatches = ['f', 'fo', 'oo', 'faoo', 'foao']
+
+    str_examples = [
+        ("Contains(1)", Contains(1)),
+        ("Contains('foo')", Contains('foo')),
+        ]
+
+    describe_examples = [("1 not in 2", 2, Contains(1))]
+
+
+class DoesNotStartWithTests(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test_describe(self):
+        mismatch = DoesNotStartWith("fo", "bo")
+        self.assertEqual("'fo' does not start with 'bo'.", mismatch.describe())
+
+    def test_describe_non_ascii_unicode(self):
+        string = _u("A\xA7")
+        suffix = _u("B\xA7")
+        mismatch = DoesNotStartWith(string, suffix)
+        self.assertEqual("%s does not start with %s." % (
+            text_repr(string), text_repr(suffix)),
+            mismatch.describe())
+
+    def test_describe_non_ascii_bytes(self):
+        string = _b("A\xA7")
+        suffix = _b("B\xA7")
+        mismatch = DoesNotStartWith(string, suffix)
+        self.assertEqual("%r does not start with %r." % (string, suffix),
+            mismatch.describe())
+
+
+class StartsWithTests(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test_str(self):
+        matcher = StartsWith("bar")
+        self.assertEqual("StartsWith('bar')", str(matcher))
+
+    def test_str_with_bytes(self):
+        b = _b("\xA7")
+        matcher = StartsWith(b)
+        self.assertEqual("StartsWith(%r)" % (b,), str(matcher))
+
+    def test_str_with_unicode(self):
+        u = _u("\xA7")
+        matcher = StartsWith(u)
+        self.assertEqual("StartsWith(%r)" % (u,), str(matcher))
+
+    def test_match(self):
+        matcher = StartsWith("bar")
+        self.assertIs(None, matcher.match("barf"))
+
+    def test_mismatch_returns_does_not_start_with(self):
+        matcher = StartsWith("bar")
+        self.assertIsInstance(matcher.match("foo"), DoesNotStartWith)
+
+    def test_mismatch_sets_matchee(self):
+        matcher = StartsWith("bar")
+        mismatch = matcher.match("foo")
+        self.assertEqual("foo", mismatch.matchee)
+
+    def test_mismatch_sets_expected(self):
+        matcher = StartsWith("bar")
+        mismatch = matcher.match("foo")
+        self.assertEqual("bar", mismatch.expected)
+
+
+class DoesNotEndWithTests(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test_describe(self):
+        mismatch = DoesNotEndWith("fo", "bo")
+        self.assertEqual("'fo' does not end with 'bo'.", mismatch.describe())
+
+    def test_describe_non_ascii_unicode(self):
+        string = _u("A\xA7")
+        suffix = _u("B\xA7")
+        mismatch = DoesNotEndWith(string, suffix)
+        self.assertEqual("%s does not end with %s." % (
+            text_repr(string), text_repr(suffix)),
+            mismatch.describe())
+
+    def test_describe_non_ascii_bytes(self):
+        string = _b("A\xA7")
+        suffix = _b("B\xA7")
+        mismatch = DoesNotEndWith(string, suffix)
+        self.assertEqual("%r does not end with %r." % (string, suffix),
+            mismatch.describe())
+
+
+class EndsWithTests(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test_str(self):
+        matcher = EndsWith("bar")
+        self.assertEqual("EndsWith('bar')", str(matcher))
+
+    def test_str_with_bytes(self):
+        b = _b("\xA7")
+        matcher = EndsWith(b)
+        self.assertEqual("EndsWith(%r)" % (b,), str(matcher))
+
+    def test_str_with_unicode(self):
+        u = _u("\xA7")
+        matcher = EndsWith(u)
+        self.assertEqual("EndsWith(%r)" % (u,), str(matcher))
+
+    def test_match(self):
+        matcher = EndsWith("arf")
+        self.assertIs(None, matcher.match("barf"))
+
+    def test_mismatch_returns_does_not_end_with(self):
+        matcher = EndsWith("bar")
+        self.assertIsInstance(matcher.match("foo"), DoesNotEndWith)
+
+    def test_mismatch_sets_matchee(self):
+        matcher = EndsWith("bar")
+        mismatch = matcher.match("foo")
+        self.assertEqual("foo", mismatch.matchee)
+
+    def test_mismatch_sets_expected(self):
+        matcher = EndsWith("bar")
+        mismatch = matcher.match("foo")
+        self.assertEqual("bar", mismatch.expected)
+
+
+class TestSameMembers(TestCase, TestMatchersInterface):
+
+    matches_matcher = SameMembers([1, 1, 2, 3, {'foo': 'bar'}])
+    matches_matches = [
+        [1, 1, 2, 3, {'foo': 'bar'}],
+        [3, {'foo': 'bar'}, 1, 2, 1],
+        [3, 2, 1, {'foo': 'bar'}, 1],
+        (2, {'foo': 'bar'}, 3, 1, 1),
+        ]
+    matches_mismatches = [
+        set([1, 2, 3]),
+        [1, 1, 2, 3, 5],
+        [1, 2, 3, {'foo': 'bar'}],
+        'foo',
+        ]
+
+    describe_examples = [
+        (("elements differ:\n"
+          "reference = ['apple', 'orange', 'canteloupe', 'watermelon', 'lemon', 'banana']\n"
+          "actual    = ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe']\n"
+          ": \n"
+          "missing:    ['watermelon']\n"
+          "extra:      ['sparrow']"
+          ),
+         ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe',],
+         SameMembers(
+             ['apple', 'orange', 'canteloupe', 'watermelon',
+              'lemon', 'banana',])),
+        ]
+
+    str_examples = [
+        ('SameMembers([1, 2, 3])', SameMembers([1, 2, 3])),
+        ]
+
+
+class TestMatchesRegex(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesRegex('a|b')
+    matches_matches = ['a', 'b']
+    matches_mismatches = ['c']
+
+    str_examples = [
+        ("MatchesRegex('a|b')", MatchesRegex('a|b')),
+        ("MatchesRegex('a|b', re.M)", MatchesRegex('a|b', re.M)),
+        ("MatchesRegex('a|b', re.I|re.M)", MatchesRegex('a|b', re.I|re.M)),
+        ("MatchesRegex(%r)" % (_b("\xA7"),), MatchesRegex(_b("\xA7"))),
+        ("MatchesRegex(%r)" % (_u("\xA7"),), MatchesRegex(_u("\xA7"))),
+        ]
+
+    describe_examples = [
+        ("'c' does not match /a|b/", 'c', MatchesRegex('a|b')),
+        ("'c' does not match /a\d/", 'c', MatchesRegex(r'a\d')),
+        ("%r does not match /\\s+\\xa7/" % (_b('c'),),
+            _b('c'), MatchesRegex(_b("\\s+\xA7"))),
+        ("%r does not match /\\s+\\xa7/" % (_u('c'),),
+            _u('c'), MatchesRegex(_u("\\s+\xA7"))),
+        ]
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)

=== renamed file 'testtools/tests/test_matchers.py' => 'testtools/tests/matchers/test_core.py'
--- testtools/tests/test_matchers.py	2012-08-10 14:05:57 +0000
+++ testtools/tests/matchers/test_core.py	2012-09-08 17:30:27 +0000
@@ -1,74 +1,25 @@
-# Copyright (c) 2008-2011 testtools developers. See LICENSE for details.
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
 
 """Tests for matchers."""
 
-import doctest
-import re
-import os
-import shutil
-import sys
-import tarfile
-import tempfile
-
 from testtools import (
     Matcher, # check that Matcher is exposed at the top level for docs.
     TestCase,
     )
 from testtools.compat import (
-    StringIO,
     str_is_unicode,
     text_repr,
-    _b,
     _u,
     )
 from testtools.matchers import (
-    AfterPreprocessing,
-    AllMatch,
-    Annotate,
-    AnnotatedMismatch,
-    _BinaryMismatch,
-    Contains,
-    ContainsAll,
-    ContainedByDict,
-    ContainsDict,
-    DirContains,
-    DirExists,
-    DocTestMatches,
-    DoesNotEndWith,
-    DoesNotStartWith,
-    EndsWith,
     Equals,
-    FileContains,
-    FileExists,
-    HasPermissions,
-    KeysEqual,
-    Is,
-    IsInstance,
-    LessThan,
-    GreaterThan,
-    MatchesAny,
-    MatchesAll,
-    MatchesAllDict,
-    MatchesDict,
     MatchesException,
-    MatchesListwise,
-    MatchesPredicate,
-    MatchesRegex,
-    MatchesSetwise,
-    MatchesStructure,
+    Raises,
+    )
+from testtools.matchers._core import (
     Mismatch,
     MismatchDecorator,
     MismatchError,
-    Not,
-    NotEquals,
-    PathExists,
-    Raises,
-    raises,
-    SameMembers,
-    SamePath,
-    StartsWith,
-    _SubDictOf,
-    TarballContains,
     )
 from testtools.tests.helpers import FullStackRunTest
 
@@ -154,925 +105,6 @@
         self.assertEqual(expected, actual)
 
 
-class Test_BinaryMismatch(TestCase):
-    """Mismatches from binary comparisons need useful describe output"""
-
-    _long_string = "This is a longish multiline non-ascii string\n\xa7"
-    _long_b = _b(_long_string)
-    _long_u = _u(_long_string)
-
-    def test_short_objects(self):
-        o1, o2 = object(), object()
-        mismatch = _BinaryMismatch(o1, "!~", o2)
-        self.assertEqual(mismatch.describe(), "%r !~ %r" % (o1, o2))
-
-    def test_short_mixed_strings(self):
-        b, u = _b("\xa7"), _u("\xa7")
-        mismatch = _BinaryMismatch(b, "!~", u)
-        self.assertEqual(mismatch.describe(), "%r !~ %r" % (b, u))
-
-    def test_long_bytes(self):
-        one_line_b = self._long_b.replace(_b("\n"), _b(" "))
-        mismatch = _BinaryMismatch(one_line_b, "!~", self._long_b)
-        self.assertEqual(mismatch.describe(),
-            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
-                text_repr(one_line_b),
-                text_repr(self._long_b, multiline=True)))
-
-    def test_long_unicode(self):
-        one_line_u = self._long_u.replace("\n", " ")
-        mismatch = _BinaryMismatch(one_line_u, "!~", self._long_u)
-        self.assertEqual(mismatch.describe(),
-            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
-                text_repr(one_line_u),
-                text_repr(self._long_u, multiline=True)))
-
-    def test_long_mixed_strings(self):
-        mismatch = _BinaryMismatch(self._long_b, "!~", self._long_u)
-        self.assertEqual(mismatch.describe(),
-            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
-                text_repr(self._long_b, multiline=True),
-                text_repr(self._long_u, multiline=True)))
-
-    def test_long_bytes_and_object(self):
-        obj = object()
-        mismatch = _BinaryMismatch(self._long_b, "!~", obj)
-        self.assertEqual(mismatch.describe(),
-            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
-                text_repr(self._long_b, multiline=True),
-                repr(obj)))
-
-    def test_long_unicode_and_object(self):
-        obj = object()
-        mismatch = _BinaryMismatch(self._long_u, "!~", obj)
-        self.assertEqual(mismatch.describe(),
-            "%s:\nreference = %s\nactual    = %s\n" % ("!~",
-                text_repr(self._long_u, multiline=True),
-                repr(obj)))
-
-
-class TestMatchersInterface(object):
-
-    run_tests_with = FullStackRunTest
-
-    def test_matches_match(self):
-        matcher = self.matches_matcher
-        matches = self.matches_matches
-        mismatches = self.matches_mismatches
-        for candidate in matches:
-            self.assertEqual(None, matcher.match(candidate))
-        for candidate in mismatches:
-            mismatch = matcher.match(candidate)
-            self.assertNotEqual(None, mismatch)
-            self.assertNotEqual(None, getattr(mismatch, 'describe', None))
-
-    def test__str__(self):
-        # [(expected, object to __str__)].
-        examples = self.str_examples
-        for expected, matcher in examples:
-            self.assertThat(matcher, DocTestMatches(expected))
-
-    def test_describe_difference(self):
-        # [(expected, matchee, matcher), ...]
-        examples = self.describe_examples
-        for difference, matchee, matcher in examples:
-            mismatch = matcher.match(matchee)
-            self.assertEqual(difference, mismatch.describe())
-
-    def test_mismatch_details(self):
-        # The mismatch object must provide get_details, which must return a
-        # dictionary mapping names to Content objects.
-        examples = self.describe_examples
-        for difference, matchee, matcher in examples:
-            mismatch = matcher.match(matchee)
-            details = mismatch.get_details()
-            self.assertEqual(dict(details), details)
-
-
-class TestDocTestMatchesInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = DocTestMatches("Ran 1 test in ...s", doctest.ELLIPSIS)
-    matches_matches = ["Ran 1 test in 0.000s", "Ran 1 test in 1.234s"]
-    matches_mismatches = ["Ran 1 tests in 0.000s", "Ran 2 test in 0.000s"]
-
-    str_examples = [("DocTestMatches('Ran 1 test in ...s\\n')",
-        DocTestMatches("Ran 1 test in ...s")),
-        ("DocTestMatches('foo\\n', flags=8)", DocTestMatches("foo", flags=8)),
-        ]
-
-    describe_examples = [('Expected:\n    Ran 1 tests in ...s\nGot:\n'
-        '    Ran 1 test in 0.123s\n', "Ran 1 test in 0.123s",
-        DocTestMatches("Ran 1 tests in ...s", doctest.ELLIPSIS))]
-
-
-class TestDocTestMatchesInterfaceUnicode(TestCase, TestMatchersInterface):
-
-    matches_matcher = DocTestMatches(_u("\xa7..."), doctest.ELLIPSIS)
-    matches_matches = [_u("\xa7"), _u("\xa7 more\n")]
-    matches_mismatches = ["\\xa7", _u("more \xa7"), _u("\n\xa7")]
-
-    str_examples = [("DocTestMatches(%r)" % (_u("\xa7\n"),),
-        DocTestMatches(_u("\xa7"))),
-        ]
-
-    describe_examples = [(
-        _u("Expected:\n    \xa7\nGot:\n    a\n"),
-        "a",
-        DocTestMatches(_u("\xa7"), doctest.ELLIPSIS))]
-
-
-class TestDocTestMatchesSpecific(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test___init__simple(self):
-        matcher = DocTestMatches("foo")
-        self.assertEqual("foo\n", matcher.want)
-
-    def test___init__flags(self):
-        matcher = DocTestMatches("bar\n", doctest.ELLIPSIS)
-        self.assertEqual("bar\n", matcher.want)
-        self.assertEqual(doctest.ELLIPSIS, matcher.flags)
-
-    def test_describe_non_ascii_bytes(self):
-        """Even with bytestrings, the mismatch should be coercible to unicode
-
-        DocTestMatches is intended for text, but the Python 2 str type also
-        permits arbitrary binary inputs. This is a slightly bogus thing to do,
-        and under Python 3 using bytes objects will reasonably raise an error.
-        """
-        header = _b("\x89PNG\r\n\x1a\n...")
-        if str_is_unicode:
-            self.assertRaises(TypeError,
-                DocTestMatches, header, doctest.ELLIPSIS)
-            return
-        matcher = DocTestMatches(header, doctest.ELLIPSIS)
-        mismatch = matcher.match(_b("GIF89a\1\0\1\0\0\0\0;"))
-        # Must be treatable as unicode text, the exact output matters less
-        self.assertTrue(unicode(mismatch.describe()))
-
-
-class TestEqualsInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = Equals(1)
-    matches_matches = [1]
-    matches_mismatches = [2]
-
-    str_examples = [("Equals(1)", Equals(1)), ("Equals('1')", Equals('1'))]
-
-    describe_examples = [("1 != 2", 2, Equals(1))]
-
-
-class TestNotEqualsInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = NotEquals(1)
-    matches_matches = [2]
-    matches_mismatches = [1]
-
-    str_examples = [
-        ("NotEquals(1)", NotEquals(1)), ("NotEquals('1')", NotEquals('1'))]
-
-    describe_examples = [("1 == 1", 1, NotEquals(1))]
-
-
-class TestIsInterface(TestCase, TestMatchersInterface):
-
-    foo = object()
-    bar = object()
-
-    matches_matcher = Is(foo)
-    matches_matches = [foo]
-    matches_mismatches = [bar, 1]
-
-    str_examples = [("Is(2)", Is(2))]
-
-    describe_examples = [("1 is not 2", 2, Is(1))]
-
-
-class TestIsInstanceInterface(TestCase, TestMatchersInterface):
-
-    class Foo:pass
-
-    matches_matcher = IsInstance(Foo)
-    matches_matches = [Foo()]
-    matches_mismatches = [object(), 1, Foo]
-
-    str_examples = [
-            ("IsInstance(str)", IsInstance(str)),
-            ("IsInstance(str, int)", IsInstance(str, int)),
-            ]
-
-    describe_examples = [
-            ("'foo' is not an instance of int", 'foo', IsInstance(int)),
-            ("'foo' is not an instance of any of (int, type)", 'foo',
-             IsInstance(int, type)),
-            ]
-
-
-class TestLessThanInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = LessThan(4)
-    matches_matches = [-5, 3]
-    matches_mismatches = [4, 5, 5000]
-
-    str_examples = [
-        ("LessThan(12)", LessThan(12)),
-        ]
-
-    describe_examples = [
-        ('4 is not > 5', 5, LessThan(4)),
-        ('4 is not > 4', 4, LessThan(4)),
-        ]
-
-
-class TestGreaterThanInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = GreaterThan(4)
-    matches_matches = [5, 8]
-    matches_mismatches = [-2, 0, 4]
-
-    str_examples = [
-        ("GreaterThan(12)", GreaterThan(12)),
-        ]
-
-    describe_examples = [
-        ('5 is not < 4', 4, GreaterThan(5)),
-        ('4 is not < 4', 4, GreaterThan(4)),
-        ]
-
-
-class TestContainsInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = Contains('foo')
-    matches_matches = ['foo', 'afoo', 'fooa']
-    matches_mismatches = ['f', 'fo', 'oo', 'faoo', 'foao']
-
-    str_examples = [
-        ("Contains(1)", Contains(1)),
-        ("Contains('foo')", Contains('foo')),
-        ]
-
-    describe_examples = [("1 not in 2", 2, Contains(1))]
-
-
-class TestContainsAllInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = ContainsAll(['foo', 'bar'])
-    matches_matches = [['foo', 'bar'], ['foo', 'z', 'bar'], ['bar', 'foo']]
-    matches_mismatches = [['f', 'g'], ['foo', 'baz'], []]
-
-    str_examples = [(
-        "MatchesAll(Contains('foo'), Contains('bar'))",
-        ContainsAll(['foo', 'bar'])),
-        ]
-
-    describe_examples = [("""Differences: [
-'baz' not in 'foo'
-]""",
-    'foo', ContainsAll(['foo', 'baz']))]
-
-
-def make_error(type, *args, **kwargs):
-    try:
-        raise type(*args, **kwargs)
-    except type:
-        return sys.exc_info()
-
-
-class TestMatchesExceptionInstanceInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesException(ValueError("foo"))
-    error_foo = make_error(ValueError, 'foo')
-    error_bar = make_error(ValueError, 'bar')
-    error_base_foo = make_error(Exception, 'foo')
-    matches_matches = [error_foo]
-    matches_mismatches = [error_bar, error_base_foo]
-
-    str_examples = [
-        ("MatchesException(Exception('foo',))",
-         MatchesException(Exception('foo')))
-        ]
-    describe_examples = [
-        ("%r is not a %r" % (Exception, ValueError),
-         error_base_foo,
-         MatchesException(ValueError("foo"))),
-        ("ValueError('bar',) has different arguments to ValueError('foo',).",
-         error_bar,
-         MatchesException(ValueError("foo"))),
-        ]
-
-
-class TestMatchesExceptionTypeInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesException(ValueError)
-    error_foo = make_error(ValueError, 'foo')
-    error_sub = make_error(UnicodeError, 'bar')
-    error_base_foo = make_error(Exception, 'foo')
-    matches_matches = [error_foo, error_sub]
-    matches_mismatches = [error_base_foo]
-
-    str_examples = [
-        ("MatchesException(%r)" % Exception,
-         MatchesException(Exception))
-        ]
-    describe_examples = [
-        ("%r is not a %r" % (Exception, ValueError),
-         error_base_foo,
-         MatchesException(ValueError)),
-        ]
-
-
-class TestMatchesExceptionTypeReInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesException(ValueError, 'fo.')
-    error_foo = make_error(ValueError, 'foo')
-    error_sub = make_error(UnicodeError, 'foo')
-    error_bar = make_error(ValueError, 'bar')
-    matches_matches = [error_foo, error_sub]
-    matches_mismatches = [error_bar]
-
-    str_examples = [
-        ("MatchesException(%r)" % Exception,
-         MatchesException(Exception, 'fo.'))
-        ]
-    describe_examples = [
-        ("'bar' does not match /fo./",
-         error_bar, MatchesException(ValueError, "fo.")),
-        ]
-
-
-class TestMatchesExceptionTypeMatcherInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesException(
-        ValueError, AfterPreprocessing(str, Equals('foo')))
-    error_foo = make_error(ValueError, 'foo')
-    error_sub = make_error(UnicodeError, 'foo')
-    error_bar = make_error(ValueError, 'bar')
-    matches_matches = [error_foo, error_sub]
-    matches_mismatches = [error_bar]
-
-    str_examples = [
-        ("MatchesException(%r)" % Exception,
-         MatchesException(Exception, Equals('foo')))
-        ]
-    describe_examples = [
-        ("5 != %r" % (error_bar[1],),
-         error_bar, MatchesException(ValueError, Equals(5))),
-        ]
-
-
-class TestNotInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = Not(Equals(1))
-    matches_matches = [2]
-    matches_mismatches = [1]
-
-    str_examples = [
-        ("Not(Equals(1))", Not(Equals(1))),
-        ("Not(Equals('1'))", Not(Equals('1')))]
-
-    describe_examples = [('1 matches Equals(1)', 1, Not(Equals(1)))]
-
-
-class TestMatchersAnyInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesAny(DocTestMatches("1"), DocTestMatches("2"))
-    matches_matches = ["1", "2"]
-    matches_mismatches = ["3"]
-
-    str_examples = [(
-        "MatchesAny(DocTestMatches('1\\n'), DocTestMatches('2\\n'))",
-        MatchesAny(DocTestMatches("1"), DocTestMatches("2"))),
-        ]
-
-    describe_examples = [("""Differences: [
-Expected:
-    1
-Got:
-    3
-
-Expected:
-    2
-Got:
-    3
-
-]""",
-        "3", MatchesAny(DocTestMatches("1"), DocTestMatches("2")))]
-
-
-class TestMatchesAllInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesAll(NotEquals(1), NotEquals(2))
-    matches_matches = [3, 4]
-    matches_mismatches = [1, 2]
-
-    str_examples = [
-        ("MatchesAll(NotEquals(1), NotEquals(2))",
-         MatchesAll(NotEquals(1), NotEquals(2)))]
-
-    describe_examples = [
-        ("""Differences: [
-1 == 1
-]""",
-         1, MatchesAll(NotEquals(1), NotEquals(2))),
-        ("1 == 1", 1,
-         MatchesAll(NotEquals(2), NotEquals(1), Equals(3), first_only=True)),
-        ]
-
-
-class TestMatchesAllDictInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)})
-    matches_matches = [3, 4]
-    matches_mismatches = [1, 2]
-
-    str_examples = [
-        ("MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)})",
-         matches_matcher)]
-
-    describe_examples = [
-        ("""a: 1 == 1""", 1, matches_matcher),
-        ]
-
-
-class TestKeysEqual(TestCase, TestMatchersInterface):
-
-    matches_matcher = KeysEqual('foo', 'bar')
-    matches_matches = [
-        {'foo': 0, 'bar': 1},
-        ]
-    matches_mismatches = [
-        {},
-        {'foo': 0},
-        {'bar': 1},
-        {'foo': 0, 'bar': 1, 'baz': 2},
-        {'a': None, 'b': None, 'c': None},
-        ]
-
-    str_examples = [
-        ("KeysEqual('foo', 'bar')", KeysEqual('foo', 'bar')),
-        ]
-
-    describe_examples = [
-        ("['bar', 'foo'] does not match {'baz': 2, 'foo': 0, 'bar': 1}: "
-         "Keys not equal",
-         {'foo': 0, 'bar': 1, 'baz': 2}, KeysEqual('foo', 'bar')),
-        ]
-
-
-class TestAnnotate(TestCase, TestMatchersInterface):
-
-    matches_matcher = Annotate("foo", Equals(1))
-    matches_matches = [1]
-    matches_mismatches = [2]
-
-    str_examples = [
-        ("Annotate('foo', Equals(1))", Annotate("foo", Equals(1)))]
-
-    describe_examples = [("1 != 2: foo", 2, Annotate('foo', Equals(1)))]
-
-    def test_if_message_no_message(self):
-        # Annotate.if_message returns the given matcher if there is no
-        # message.
-        matcher = Equals(1)
-        not_annotated = Annotate.if_message('', matcher)
-        self.assertIs(matcher, not_annotated)
-
-    def test_if_message_given_message(self):
-        # Annotate.if_message returns an annotated version of the matcher if a
-        # message is provided.
-        matcher = Equals(1)
-        expected = Annotate('foo', matcher)
-        annotated = Annotate.if_message('foo', matcher)
-        self.assertThat(
-            annotated,
-            MatchesStructure.fromExample(expected, 'annotation', 'matcher'))
-
-
-class TestAnnotatedMismatch(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test_forwards_details(self):
-        x = Mismatch('description', {'foo': 'bar'})
-        annotated = AnnotatedMismatch("annotation", x)
-        self.assertEqual(x.get_details(), annotated.get_details())
-
-
-class TestRaisesInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = Raises()
-    def boom():
-        raise Exception('foo')
-    matches_matches = [boom]
-    matches_mismatches = [lambda:None]
-
-    # Tricky to get function objects to render constantly, and the interfaces
-    # helper uses assertEqual rather than (for instance) DocTestMatches.
-    str_examples = []
-
-    describe_examples = []
-
-
-class TestRaisesExceptionMatcherInterface(TestCase, TestMatchersInterface):
-
-    matches_matcher = Raises(
-        exception_matcher=MatchesException(Exception('foo')))
-    def boom_bar():
-        raise Exception('bar')
-    def boom_foo():
-        raise Exception('foo')
-    matches_matches = [boom_foo]
-    matches_mismatches = [lambda:None, boom_bar]
-
-    # Tricky to get function objects to render constantly, and the interfaces
-    # helper uses assertEqual rather than (for instance) DocTestMatches.
-    str_examples = []
-
-    describe_examples = []
-
-
-class TestRaisesBaseTypes(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def raiser(self):
-        raise KeyboardInterrupt('foo')
-
-    def test_KeyboardInterrupt_matched(self):
-        # When KeyboardInterrupt is matched, it is swallowed.
-        matcher = Raises(MatchesException(KeyboardInterrupt))
-        self.assertThat(self.raiser, matcher)
-
-    def test_KeyboardInterrupt_propogates(self):
-        # The default 'it raised' propogates KeyboardInterrupt.
-        match_keyb = Raises(MatchesException(KeyboardInterrupt))
-        def raise_keyb_from_match():
-            matcher = Raises()
-            matcher.match(self.raiser)
-        self.assertThat(raise_keyb_from_match, match_keyb)
-
-    def test_KeyboardInterrupt_match_Exception_propogates(self):
-        # If the raised exception isn't matched, and it is not a subclass of
-        # Exception, it is propogated.
-        match_keyb = Raises(MatchesException(KeyboardInterrupt))
-        def raise_keyb_from_match():
-            if sys.version_info > (2, 5):
-                matcher = Raises(MatchesException(Exception))
-            else:
-                # On Python 2.4 KeyboardInterrupt is a StandardError subclass
-                # but should propogate from less generic exception matchers
-                matcher = Raises(MatchesException(EnvironmentError))
-            matcher.match(self.raiser)
-        self.assertThat(raise_keyb_from_match, match_keyb)
-
-
-class TestRaisesConvenience(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test_exc_type(self):
-        self.assertThat(lambda: 1/0, raises(ZeroDivisionError))
-
-    def test_exc_value(self):
-        e = RuntimeError("You lose!")
-        def raiser():
-            raise e
-        self.assertThat(raiser, raises(e))
-
-
-class DoesNotStartWithTests(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test_describe(self):
-        mismatch = DoesNotStartWith("fo", "bo")
-        self.assertEqual("'fo' does not start with 'bo'.", mismatch.describe())
-
-    def test_describe_non_ascii_unicode(self):
-        string = _u("A\xA7")
-        suffix = _u("B\xA7")
-        mismatch = DoesNotStartWith(string, suffix)
-        self.assertEqual("%s does not start with %s." % (
-            text_repr(string), text_repr(suffix)),
-            mismatch.describe())
-
-    def test_describe_non_ascii_bytes(self):
-        string = _b("A\xA7")
-        suffix = _b("B\xA7")
-        mismatch = DoesNotStartWith(string, suffix)
-        self.assertEqual("%r does not start with %r." % (string, suffix),
-            mismatch.describe())
-
-
-class StartsWithTests(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test_str(self):
-        matcher = StartsWith("bar")
-        self.assertEqual("StartsWith('bar')", str(matcher))
-
-    def test_str_with_bytes(self):
-        b = _b("\xA7")
-        matcher = StartsWith(b)
-        self.assertEqual("StartsWith(%r)" % (b,), str(matcher))
-
-    def test_str_with_unicode(self):
-        u = _u("\xA7")
-        matcher = StartsWith(u)
-        self.assertEqual("StartsWith(%r)" % (u,), str(matcher))
-
-    def test_match(self):
-        matcher = StartsWith("bar")
-        self.assertIs(None, matcher.match("barf"))
-
-    def test_mismatch_returns_does_not_start_with(self):
-        matcher = StartsWith("bar")
-        self.assertIsInstance(matcher.match("foo"), DoesNotStartWith)
-
-    def test_mismatch_sets_matchee(self):
-        matcher = StartsWith("bar")
-        mismatch = matcher.match("foo")
-        self.assertEqual("foo", mismatch.matchee)
-
-    def test_mismatch_sets_expected(self):
-        matcher = StartsWith("bar")
-        mismatch = matcher.match("foo")
-        self.assertEqual("bar", mismatch.expected)
-
-
-class DoesNotEndWithTests(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test_describe(self):
-        mismatch = DoesNotEndWith("fo", "bo")
-        self.assertEqual("'fo' does not end with 'bo'.", mismatch.describe())
-
-    def test_describe_non_ascii_unicode(self):
-        string = _u("A\xA7")
-        suffix = _u("B\xA7")
-        mismatch = DoesNotEndWith(string, suffix)
-        self.assertEqual("%s does not end with %s." % (
-            text_repr(string), text_repr(suffix)),
-            mismatch.describe())
-
-    def test_describe_non_ascii_bytes(self):
-        string = _b("A\xA7")
-        suffix = _b("B\xA7")
-        mismatch = DoesNotEndWith(string, suffix)
-        self.assertEqual("%r does not end with %r." % (string, suffix),
-            mismatch.describe())
-
-
-class EndsWithTests(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test_str(self):
-        matcher = EndsWith("bar")
-        self.assertEqual("EndsWith('bar')", str(matcher))
-
-    def test_str_with_bytes(self):
-        b = _b("\xA7")
-        matcher = EndsWith(b)
-        self.assertEqual("EndsWith(%r)" % (b,), str(matcher))
-
-    def test_str_with_unicode(self):
-        u = _u("\xA7")
-        matcher = EndsWith(u)
-        self.assertEqual("EndsWith(%r)" % (u,), str(matcher))
-
-    def test_match(self):
-        matcher = EndsWith("arf")
-        self.assertIs(None, matcher.match("barf"))
-
-    def test_mismatch_returns_does_not_end_with(self):
-        matcher = EndsWith("bar")
-        self.assertIsInstance(matcher.match("foo"), DoesNotEndWith)
-
-    def test_mismatch_sets_matchee(self):
-        matcher = EndsWith("bar")
-        mismatch = matcher.match("foo")
-        self.assertEqual("foo", mismatch.matchee)
-
-    def test_mismatch_sets_expected(self):
-        matcher = EndsWith("bar")
-        mismatch = matcher.match("foo")
-        self.assertEqual("bar", mismatch.expected)
-
-
-def run_doctest(obj, name):
-    p = doctest.DocTestParser()
-    t = p.get_doctest(
-        obj.__doc__, sys.modules[obj.__module__].__dict__, name, '', 0)
-    r = doctest.DocTestRunner()
-    output = StringIO()
-    r.run(t, out=output.write)
-    return r.failures, output.getvalue()
-
-
-class TestMatchesListwise(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def test_docstring(self):
-        failure_count, output = run_doctest(
-            MatchesListwise, "MatchesListwise")
-        if failure_count:
-            self.fail("Doctest failed with %s" % output)
-
-
-class TestMatchesStructure(TestCase, TestMatchersInterface):
-
-    class SimpleClass:
-        def __init__(self, x, y):
-            self.x = x
-            self.y = y
-
-    matches_matcher = MatchesStructure(x=Equals(1), y=Equals(2))
-    matches_matches = [SimpleClass(1, 2)]
-    matches_mismatches = [
-        SimpleClass(2, 2),
-        SimpleClass(1, 1),
-        SimpleClass(3, 3),
-        ]
-
-    str_examples = [
-        ("MatchesStructure(x=Equals(1))", MatchesStructure(x=Equals(1))),
-        ("MatchesStructure(y=Equals(2))", MatchesStructure(y=Equals(2))),
-        ("MatchesStructure(x=Equals(1), y=Equals(2))",
-         MatchesStructure(x=Equals(1), y=Equals(2))),
-        ]
-
-    describe_examples = [
-        ("""\
-Differences: [
-3 != 1: x
-]""", SimpleClass(1, 2), MatchesStructure(x=Equals(3), y=Equals(2))),
-        ("""\
-Differences: [
-3 != 2: y
-]""", SimpleClass(1, 2), MatchesStructure(x=Equals(1), y=Equals(3))),
-        ("""\
-Differences: [
-0 != 1: x
-0 != 2: y
-]""", SimpleClass(1, 2), MatchesStructure(x=Equals(0), y=Equals(0))),
-        ]
-
-    def test_fromExample(self):
-        self.assertThat(
-            self.SimpleClass(1, 2),
-            MatchesStructure.fromExample(self.SimpleClass(1, 3), 'x'))
-
-    def test_byEquality(self):
-        self.assertThat(
-            self.SimpleClass(1, 2),
-            MatchesStructure.byEquality(x=1))
-
-    def test_withStructure(self):
-        self.assertThat(
-            self.SimpleClass(1, 2),
-            MatchesStructure.byMatcher(LessThan, x=2))
-
-    def test_update(self):
-        self.assertThat(
-            self.SimpleClass(1, 2),
-            MatchesStructure(x=NotEquals(1)).update(x=Equals(1)))
-
-    def test_update_none(self):
-        self.assertThat(
-            self.SimpleClass(1, 2),
-            MatchesStructure(x=Equals(1), z=NotEquals(42)).update(
-                z=None))
-
-
-class TestMatchesRegex(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesRegex('a|b')
-    matches_matches = ['a', 'b']
-    matches_mismatches = ['c']
-
-    str_examples = [
-        ("MatchesRegex('a|b')", MatchesRegex('a|b')),
-        ("MatchesRegex('a|b', re.M)", MatchesRegex('a|b', re.M)),
-        ("MatchesRegex('a|b', re.I|re.M)", MatchesRegex('a|b', re.I|re.M)),
-        ("MatchesRegex(%r)" % (_b("\xA7"),), MatchesRegex(_b("\xA7"))),
-        ("MatchesRegex(%r)" % (_u("\xA7"),), MatchesRegex(_u("\xA7"))),
-        ]
-
-    describe_examples = [
-        ("'c' does not match /a|b/", 'c', MatchesRegex('a|b')),
-        ("'c' does not match /a\d/", 'c', MatchesRegex(r'a\d')),
-        ("%r does not match /\\s+\\xa7/" % (_b('c'),),
-            _b('c'), MatchesRegex(_b("\\s+\xA7"))),
-        ("%r does not match /\\s+\\xa7/" % (_u('c'),),
-            _u('c'), MatchesRegex(_u("\\s+\xA7"))),
-        ]
-
-
-class TestMatchesSetwise(TestCase):
-
-    run_tests_with = FullStackRunTest
-
-    def assertMismatchWithDescriptionMatching(self, value, matcher,
-                                              description_matcher):
-        mismatch = matcher.match(value)
-        if mismatch is None:
-            self.fail("%s matched %s" % (matcher, value))
-        actual_description = mismatch.describe()
-        self.assertThat(
-            actual_description,
-            Annotate(
-                "%s matching %s" % (matcher, value),
-                description_matcher))
-
-    def test_matches(self):
-        self.assertIs(
-            None, MatchesSetwise(Equals(1), Equals(2)).match([2, 1]))
-
-    def test_mismatches(self):
-        self.assertMismatchWithDescriptionMatching(
-            [2, 3], MatchesSetwise(Equals(1), Equals(2)),
-            MatchesRegex('.*There was 1 mismatch$', re.S))
-
-    def test_too_many_matchers(self):
-        self.assertMismatchWithDescriptionMatching(
-            [2, 3], MatchesSetwise(Equals(1), Equals(2), Equals(3)),
-            Equals('There was 1 matcher left over: Equals(1)'))
-
-    def test_too_many_values(self):
-        self.assertMismatchWithDescriptionMatching(
-            [1, 2, 3], MatchesSetwise(Equals(1), Equals(2)),
-            Equals('There was 1 value left over: [3]'))
-
-    def test_two_too_many_matchers(self):
-        self.assertMismatchWithDescriptionMatching(
-            [3], MatchesSetwise(Equals(1), Equals(2), Equals(3)),
-            MatchesRegex(
-                'There were 2 matchers left over: Equals\([12]\), '
-                'Equals\([12]\)'))
-
-    def test_two_too_many_values(self):
-        self.assertMismatchWithDescriptionMatching(
-            [1, 2, 3, 4], MatchesSetwise(Equals(1), Equals(2)),
-            MatchesRegex(
-                'There were 2 values left over: \[[34], [34]\]'))
-
-    def test_mismatch_and_too_many_matchers(self):
-        self.assertMismatchWithDescriptionMatching(
-            [2, 3], MatchesSetwise(Equals(0), Equals(1), Equals(2)),
-            MatchesRegex(
-                '.*There was 1 mismatch and 1 extra matcher: Equals\([01]\)',
-                re.S))
-
-    def test_mismatch_and_too_many_values(self):
-        self.assertMismatchWithDescriptionMatching(
-            [2, 3, 4], MatchesSetwise(Equals(1), Equals(2)),
-            MatchesRegex(
-                '.*There was 1 mismatch and 1 extra value: \[[34]\]',
-                re.S))
-
-    def test_mismatch_and_two_too_many_matchers(self):
-        self.assertMismatchWithDescriptionMatching(
-            [3, 4], MatchesSetwise(
-                Equals(0), Equals(1), Equals(2), Equals(3)),
-            MatchesRegex(
-                '.*There was 1 mismatch and 2 extra matchers: '
-                'Equals\([012]\), Equals\([012]\)', re.S))
-
-    def test_mismatch_and_two_too_many_values(self):
-        self.assertMismatchWithDescriptionMatching(
-            [2, 3, 4, 5], MatchesSetwise(Equals(1), Equals(2)),
-            MatchesRegex(
-                '.*There was 1 mismatch and 2 extra values: \[[145], [145]\]',
-                re.S))
-
-
-class TestAfterPreprocessing(TestCase, TestMatchersInterface):
-
-    def parity(x):
-        return x % 2
-
-    matches_matcher = AfterPreprocessing(parity, Equals(1))
-    matches_matches = [3, 5]
-    matches_mismatches = [2]
-
-    str_examples = [
-        ("AfterPreprocessing(<function parity>, Equals(1))",
-         AfterPreprocessing(parity, Equals(1))),
-        ]
-
-    describe_examples = [
-        ("1 != 0: after <function parity> on 2", 2,
-         AfterPreprocessing(parity, Equals(1))),
-        ("1 != 0", 2,
-         AfterPreprocessing(parity, Equals(1), annotate=False)),
-        ]
-
-
 class TestMismatchDecorator(TestCase):
 
     run_tests_with = FullStackRunTest
@@ -1095,462 +127,6 @@
             repr(decorated))
 
 
-class TestAllMatch(TestCase, TestMatchersInterface):
-
-    matches_matcher = AllMatch(LessThan(10))
-    matches_matches = [
-        [9, 9, 9],
-        (9, 9),
-        iter([9, 9, 9, 9, 9]),
-        ]
-    matches_mismatches = [
-        [11, 9, 9],
-        iter([9, 12, 9, 11]),
-        ]
-
-    str_examples = [
-        ("AllMatch(LessThan(12))", AllMatch(LessThan(12))),
-        ]
-
-    describe_examples = [
-        ('Differences: [\n'
-         '10 is not > 11\n'
-         '10 is not > 10\n'
-         ']',
-         [11, 9, 10],
-         AllMatch(LessThan(10))),
-        ]
-
-
-class TestSameMembers(TestCase, TestMatchersInterface):
-
-    matches_matcher = SameMembers([1, 1, 2, 3, {'foo': 'bar'}])
-    matches_matches = [
-        [1, 1, 2, 3, {'foo': 'bar'}],
-        [3, {'foo': 'bar'}, 1, 2, 1],
-        [3, 2, 1, {'foo': 'bar'}, 1],
-        (2, {'foo': 'bar'}, 3, 1, 1),
-        ]
-    matches_mismatches = [
-        set([1, 2, 3]),
-        [1, 1, 2, 3, 5],
-        [1, 2, 3, {'foo': 'bar'}],
-        'foo',
-        ]
-
-    describe_examples = [
-        (("elements differ:\n"
-          "reference = ['apple', 'orange', 'canteloupe', 'watermelon', 'lemon', 'banana']\n"
-          "actual    = ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe']\n"
-          ": \n"
-          "missing:    ['watermelon']\n"
-          "extra:      ['sparrow']"
-          ),
-         ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe',],
-         SameMembers(
-             ['apple', 'orange', 'canteloupe', 'watermelon',
-              'lemon', 'banana',])),
-        ]
-
-    str_examples = [
-        ('SameMembers([1, 2, 3])', SameMembers([1, 2, 3])),
-        ]
-
-
-class PathHelpers(object):
-
-    def mkdtemp(self):
-        directory = tempfile.mkdtemp()
-        self.addCleanup(shutil.rmtree, directory)
-        return directory
-
-    def create_file(self, filename, contents=''):
-        fp = open(filename, 'w')
-        try:
-            fp.write(contents)
-        finally:
-            fp.close()
-
-    def touch(self, filename):
-        return self.create_file(filename)
-
-
-class TestPathExists(TestCase, PathHelpers):
-
-    def test_exists(self):
-        tempdir = self.mkdtemp()
-        self.assertThat(tempdir, PathExists())
-
-    def test_not_exists(self):
-        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
-        mismatch = PathExists().match(doesntexist)
-        self.assertThat(
-            "%s does not exist." % doesntexist, Equals(mismatch.describe()))
-
-
-class TestDirExists(TestCase, PathHelpers):
-
-    def test_exists(self):
-        tempdir = self.mkdtemp()
-        self.assertThat(tempdir, DirExists())
-
-    def test_not_exists(self):
-        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
-        mismatch = DirExists().match(doesntexist)
-        self.assertThat(
-            PathExists().match(doesntexist).describe(),
-            Equals(mismatch.describe()))
-
-    def test_not_a_directory(self):
-        filename = os.path.join(self.mkdtemp(), 'foo')
-        self.touch(filename)
-        mismatch = DirExists().match(filename)
-        self.assertThat(
-            "%s is not a directory." % filename, Equals(mismatch.describe()))
-
-
-class TestFileExists(TestCase, PathHelpers):
-
-    def test_exists(self):
-        tempdir = self.mkdtemp()
-        filename = os.path.join(tempdir, 'filename')
-        self.touch(filename)
-        self.assertThat(filename, FileExists())
-
-    def test_not_exists(self):
-        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
-        mismatch = FileExists().match(doesntexist)
-        self.assertThat(
-            PathExists().match(doesntexist).describe(),
-            Equals(mismatch.describe()))
-
-    def test_not_a_file(self):
-        tempdir = self.mkdtemp()
-        mismatch = FileExists().match(tempdir)
-        self.assertThat(
-            "%s is not a file." % tempdir, Equals(mismatch.describe()))
-
-
-class TestDirContains(TestCase, PathHelpers):
-
-    def test_empty(self):
-        tempdir = self.mkdtemp()
-        self.assertThat(tempdir, DirContains([]))
-
-    def test_not_exists(self):
-        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
-        mismatch = DirContains([]).match(doesntexist)
-        self.assertThat(
-            PathExists().match(doesntexist).describe(),
-            Equals(mismatch.describe()))
-
-    def test_contains_files(self):
-        tempdir = self.mkdtemp()
-        self.touch(os.path.join(tempdir, 'foo'))
-        self.touch(os.path.join(tempdir, 'bar'))
-        self.assertThat(tempdir, DirContains(['bar', 'foo']))
-
-    def test_matcher(self):
-        tempdir = self.mkdtemp()
-        self.touch(os.path.join(tempdir, 'foo'))
-        self.touch(os.path.join(tempdir, 'bar'))
-        self.assertThat(tempdir, DirContains(matcher=Contains('bar')))
-
-    def test_neither_specified(self):
-        self.assertRaises(AssertionError, DirContains)
-
-    def test_both_specified(self):
-        self.assertRaises(
-            AssertionError, DirContains, filenames=[], matcher=Contains('a'))
-
-    def test_does_not_contain_files(self):
-        tempdir = self.mkdtemp()
-        self.touch(os.path.join(tempdir, 'foo'))
-        mismatch = DirContains(['bar', 'foo']).match(tempdir)
-        self.assertThat(
-            Equals(['bar', 'foo']).match(['foo']).describe(),
-            Equals(mismatch.describe()))
-
-
-class TestFileContains(TestCase, PathHelpers):
-
-    def test_not_exists(self):
-        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
-        mismatch = FileContains('').match(doesntexist)
-        self.assertThat(
-            PathExists().match(doesntexist).describe(),
-            Equals(mismatch.describe()))
-
-    def test_contains(self):
-        tempdir = self.mkdtemp()
-        filename = os.path.join(tempdir, 'foo')
-        self.create_file(filename, 'Hello World!')
-        self.assertThat(filename, FileContains('Hello World!'))
-
-    def test_matcher(self):
-        tempdir = self.mkdtemp()
-        filename = os.path.join(tempdir, 'foo')
-        self.create_file(filename, 'Hello World!')
-        self.assertThat(
-            filename, FileContains(matcher=DocTestMatches('Hello World!')))
-
-    def test_neither_specified(self):
-        self.assertRaises(AssertionError, FileContains)
-
-    def test_both_specified(self):
-        self.assertRaises(
-            AssertionError, FileContains, contents=[], matcher=Contains('a'))
-
-    def test_does_not_contain(self):
-        tempdir = self.mkdtemp()
-        filename = os.path.join(tempdir, 'foo')
-        self.create_file(filename, 'Goodbye Cruel World!')
-        mismatch = FileContains('Hello World!').match(filename)
-        self.assertThat(
-            Equals('Hello World!').match('Goodbye Cruel World!').describe(),
-            Equals(mismatch.describe()))
-
-
-def is_even(x):
-    return x % 2 == 0
-
-
-class TestMatchesPredicate(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesPredicate(is_even, "%s is not even")
-    matches_matches = [2, 4, 6, 8]
-    matches_mismatches = [3, 5, 7, 9]
-
-    str_examples = [
-        ("MatchesPredicate(%r, %r)" % (is_even, "%s is not even"),
-         MatchesPredicate(is_even, "%s is not even")),
-        ]
-
-    describe_examples = [
-        ('7 is not even', 7, MatchesPredicate(is_even, "%s is not even")),
-        ]
-
-
-class TestTarballContains(TestCase, PathHelpers):
-
-    def test_match(self):
-        tempdir = self.mkdtemp()
-        in_temp_dir = lambda x: os.path.join(tempdir, x)
-        self.touch(in_temp_dir('a'))
-        self.touch(in_temp_dir('b'))
-        tarball = tarfile.open(in_temp_dir('foo.tar.gz'), 'w')
-        tarball.add(in_temp_dir('a'), 'a')
-        tarball.add(in_temp_dir('b'), 'b')
-        tarball.close()
-        self.assertThat(
-            in_temp_dir('foo.tar.gz'), TarballContains(['b', 'a']))
-
-    def test_mismatch(self):
-        tempdir = self.mkdtemp()
-        in_temp_dir = lambda x: os.path.join(tempdir, x)
-        self.touch(in_temp_dir('a'))
-        self.touch(in_temp_dir('b'))
-        tarball = tarfile.open(in_temp_dir('foo.tar.gz'), 'w')
-        tarball.add(in_temp_dir('a'), 'a')
-        tarball.add(in_temp_dir('b'), 'b')
-        tarball.close()
-        mismatch = TarballContains(['d', 'c']).match(in_temp_dir('foo.tar.gz'))
-        self.assertEqual(
-            mismatch.describe(),
-            Equals(['c', 'd']).match(['a', 'b']).describe())
-
-
-class TestSamePath(TestCase, PathHelpers):
-
-    def test_same_string(self):
-        self.assertThat('foo', SamePath('foo'))
-
-    def test_relative_and_absolute(self):
-        path = 'foo'
-        abspath = os.path.abspath(path)
-        self.assertThat(path, SamePath(abspath))
-        self.assertThat(abspath, SamePath(path))
-
-    def test_real_path(self):
-        tempdir = self.mkdtemp()
-        source = os.path.join(tempdir, 'source')
-        self.touch(source)
-        target = os.path.join(tempdir, 'target')
-        try:
-            os.symlink(source, target)
-        except (AttributeError, NotImplementedError):
-            self.skip("No symlink support")
-        self.assertThat(source, SamePath(target))
-        self.assertThat(target, SamePath(source))
-
-
-class TestHasPermissions(TestCase, PathHelpers):
-
-    def test_match(self):
-        tempdir = self.mkdtemp()
-        filename = os.path.join(tempdir, 'filename')
-        self.touch(filename)
-        permissions = oct(os.stat(filename).st_mode)[-4:]
-        self.assertThat(filename, HasPermissions(permissions))
-
-
-class TestSubDictOf(TestCase, TestMatchersInterface):
-
-    matches_matcher = _SubDictOf({'foo': 'bar', 'baz': 'qux'})
-
-    matches_matches = [
-        {'foo': 'bar', 'baz': 'qux'},
-        {'foo': 'bar'},
-        ]
-
-    matches_mismatches = [
-        {'foo': 'bar', 'baz': 'qux', 'cat': 'dog'},
-        {'foo': 'bar', 'cat': 'dog'},
-        ]
-
-    str_examples = []
-    describe_examples = []
-
-
-class TestMatchesDict(TestCase, TestMatchersInterface):
-
-    matches_matcher = MatchesDict(
-        {'foo': Equals('bar'), 'baz': Not(Equals('qux'))})
-
-    matches_matches = [
-        {'foo': 'bar', 'baz': None},
-        {'foo': 'bar', 'baz': 'quux'},
-        ]
-    matches_mismatches = [
-        {},
-        {'foo': 'bar', 'baz': 'qux'},
-        {'foo': 'bop', 'baz': 'qux'},
-        {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'},
-        {'foo': 'bar', 'cat': 'dog'},
-        ]
-
-    str_examples = [
-        ("MatchesDict({'foo': %s, 'baz': %s})" % (
-                Equals('bar'), Not(Equals('qux'))),
-         matches_matcher),
-        ]
-
-    describe_examples = [
-        ("Missing: {\n"
-         "  'baz': Not(Equals('qux')),\n"
-         "  'foo': Equals('bar'),\n"
-         "}",
-         {}, matches_matcher),
-        ("Differences: {\n"
-         "  'baz': 'qux' matches Equals('qux'),\n"
-         "}",
-         {'foo': 'bar', 'baz': 'qux'}, matches_matcher),
-        ("Differences: {\n"
-         "  'baz': 'qux' matches Equals('qux'),\n"
-         "  'foo': 'bar' != 'bop',\n"
-         "}",
-         {'foo': 'bop', 'baz': 'qux'}, matches_matcher),
-        ("Extra: {\n"
-         "  'cat': 'dog',\n"
-         "}",
-         {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'}, matches_matcher),
-        ("Extra: {\n"
-         "  'cat': 'dog',\n"
-         "}\n"
-         "Missing: {\n"
-         "  'baz': Not(Equals('qux')),\n"
-         "}",
-         {'foo': 'bar', 'cat': 'dog'}, matches_matcher),
-        ]
-
-
-class TestContainsDict(TestCase, TestMatchersInterface):
-
-    matches_matcher = ContainsDict(
-        {'foo': Equals('bar'), 'baz': Not(Equals('qux'))})
-
-    matches_matches = [
-        {'foo': 'bar', 'baz': None},
-        {'foo': 'bar', 'baz': 'quux'},
-        {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'},
-        ]
-    matches_mismatches = [
-        {},
-        {'foo': 'bar', 'baz': 'qux'},
-        {'foo': 'bop', 'baz': 'qux'},
-        {'foo': 'bar', 'cat': 'dog'},
-        {'foo': 'bar'},
-        ]
-
-    str_examples = [
-        ("ContainsDict({'foo': %s, 'baz': %s})" % (
-                Equals('bar'), Not(Equals('qux'))),
-         matches_matcher),
-        ]
-
-    describe_examples = [
-        ("Missing: {\n"
-         "  'baz': Not(Equals('qux')),\n"
-         "  'foo': Equals('bar'),\n"
-         "}",
-         {}, matches_matcher),
-        ("Differences: {\n"
-         "  'baz': 'qux' matches Equals('qux'),\n"
-         "}",
-         {'foo': 'bar', 'baz': 'qux'}, matches_matcher),
-        ("Differences: {\n"
-         "  'baz': 'qux' matches Equals('qux'),\n"
-         "  'foo': 'bar' != 'bop',\n"
-         "}",
-         {'foo': 'bop', 'baz': 'qux'}, matches_matcher),
-        ("Missing: {\n"
-         "  'baz': Not(Equals('qux')),\n"
-         "}",
-         {'foo': 'bar', 'cat': 'dog'}, matches_matcher),
-        ]
-
-
-class TestContainedByDict(TestCase, TestMatchersInterface):
-
-    matches_matcher = ContainedByDict(
-        {'foo': Equals('bar'), 'baz': Not(Equals('qux'))})
-
-    matches_matches = [
-        {},
-        {'foo': 'bar'},
-        {'foo': 'bar', 'baz': 'quux'},
-        {'baz': 'quux'},
-        ]
-    matches_mismatches = [
-        {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'},
-        {'foo': 'bar', 'baz': 'qux'},
-        {'foo': 'bop', 'baz': 'qux'},
-        {'foo': 'bar', 'cat': 'dog'},
-        ]
-
-    str_examples = [
-        ("ContainedByDict({'foo': %s, 'baz': %s})" % (
-                Equals('bar'), Not(Equals('qux'))),
-         matches_matcher),
-        ]
-
-    describe_examples = [
-        ("Differences: {\n"
-         "  'baz': 'qux' matches Equals('qux'),\n"
-         "}",
-         {'foo': 'bar', 'baz': 'qux'}, matches_matcher),
-        ("Differences: {\n"
-         "  'baz': 'qux' matches Equals('qux'),\n"
-         "  'foo': 'bar' != 'bop',\n"
-         "}",
-         {'foo': 'bop', 'baz': 'qux'}, matches_matcher),
-        ("Extra: {\n"
-         "  'cat': 'dog',\n"
-         "}",
-         {'foo': 'bar', 'cat': 'dog'}, matches_matcher),
-        ]
-
-
 def test_suite():
     from unittest import TestLoader
     return TestLoader().loadTestsFromName(__name__)

=== added file 'testtools/tests/matchers/test_datastructures.py'
--- testtools/tests/matchers/test_datastructures.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/test_datastructures.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,209 @@
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
+
+import doctest
+import re
+import sys
+
+from testtools import TestCase
+from testtools.compat import StringIO
+from testtools.matchers import (
+    Annotate,
+    Equals,
+    LessThan,
+    MatchesRegex,
+    NotEquals,
+    )
+from testtools.matchers._datastructures import (
+    ContainsAll,
+    MatchesListwise,
+    MatchesStructure,
+    MatchesSetwise,
+    )
+from testtools.tests.helpers import FullStackRunTest
+from testtools.tests.matchers.helpers import TestMatchersInterface
+
+
+def run_doctest(obj, name):
+    p = doctest.DocTestParser()
+    t = p.get_doctest(
+        obj.__doc__, sys.modules[obj.__module__].__dict__, name, '', 0)
+    r = doctest.DocTestRunner()
+    output = StringIO()
+    r.run(t, out=output.write)
+    return r.failures, output.getvalue()
+
+
+class TestMatchesListwise(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test_docstring(self):
+        failure_count, output = run_doctest(
+            MatchesListwise, "MatchesListwise")
+        if failure_count:
+            self.fail("Doctest failed with %s" % output)
+
+
+class TestMatchesStructure(TestCase, TestMatchersInterface):
+
+    class SimpleClass:
+        def __init__(self, x, y):
+            self.x = x
+            self.y = y
+
+    matches_matcher = MatchesStructure(x=Equals(1), y=Equals(2))
+    matches_matches = [SimpleClass(1, 2)]
+    matches_mismatches = [
+        SimpleClass(2, 2),
+        SimpleClass(1, 1),
+        SimpleClass(3, 3),
+        ]
+
+    str_examples = [
+        ("MatchesStructure(x=Equals(1))", MatchesStructure(x=Equals(1))),
+        ("MatchesStructure(y=Equals(2))", MatchesStructure(y=Equals(2))),
+        ("MatchesStructure(x=Equals(1), y=Equals(2))",
+         MatchesStructure(x=Equals(1), y=Equals(2))),
+        ]
+
+    describe_examples = [
+        ("""\
+Differences: [
+3 != 1: x
+]""", SimpleClass(1, 2), MatchesStructure(x=Equals(3), y=Equals(2))),
+        ("""\
+Differences: [
+3 != 2: y
+]""", SimpleClass(1, 2), MatchesStructure(x=Equals(1), y=Equals(3))),
+        ("""\
+Differences: [
+0 != 1: x
+0 != 2: y
+]""", SimpleClass(1, 2), MatchesStructure(x=Equals(0), y=Equals(0))),
+        ]
+
+    def test_fromExample(self):
+        self.assertThat(
+            self.SimpleClass(1, 2),
+            MatchesStructure.fromExample(self.SimpleClass(1, 3), 'x'))
+
+    def test_byEquality(self):
+        self.assertThat(
+            self.SimpleClass(1, 2),
+            MatchesStructure.byEquality(x=1))
+
+    def test_withStructure(self):
+        self.assertThat(
+            self.SimpleClass(1, 2),
+            MatchesStructure.byMatcher(LessThan, x=2))
+
+    def test_update(self):
+        self.assertThat(
+            self.SimpleClass(1, 2),
+            MatchesStructure(x=NotEquals(1)).update(x=Equals(1)))
+
+    def test_update_none(self):
+        self.assertThat(
+            self.SimpleClass(1, 2),
+            MatchesStructure(x=Equals(1), z=NotEquals(42)).update(
+                z=None))
+
+
+class TestMatchesSetwise(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def assertMismatchWithDescriptionMatching(self, value, matcher,
+                                              description_matcher):
+        mismatch = matcher.match(value)
+        if mismatch is None:
+            self.fail("%s matched %s" % (matcher, value))
+        actual_description = mismatch.describe()
+        self.assertThat(
+            actual_description,
+            Annotate(
+                "%s matching %s" % (matcher, value),
+                description_matcher))
+
+    def test_matches(self):
+        self.assertIs(
+            None, MatchesSetwise(Equals(1), Equals(2)).match([2, 1]))
+
+    def test_mismatches(self):
+        self.assertMismatchWithDescriptionMatching(
+            [2, 3], MatchesSetwise(Equals(1), Equals(2)),
+            MatchesRegex('.*There was 1 mismatch$', re.S))
+
+    def test_too_many_matchers(self):
+        self.assertMismatchWithDescriptionMatching(
+            [2, 3], MatchesSetwise(Equals(1), Equals(2), Equals(3)),
+            Equals('There was 1 matcher left over: Equals(1)'))
+
+    def test_too_many_values(self):
+        self.assertMismatchWithDescriptionMatching(
+            [1, 2, 3], MatchesSetwise(Equals(1), Equals(2)),
+            Equals('There was 1 value left over: [3]'))
+
+    def test_two_too_many_matchers(self):
+        self.assertMismatchWithDescriptionMatching(
+            [3], MatchesSetwise(Equals(1), Equals(2), Equals(3)),
+            MatchesRegex(
+                'There were 2 matchers left over: Equals\([12]\), '
+                'Equals\([12]\)'))
+
+    def test_two_too_many_values(self):
+        self.assertMismatchWithDescriptionMatching(
+            [1, 2, 3, 4], MatchesSetwise(Equals(1), Equals(2)),
+            MatchesRegex(
+                'There were 2 values left over: \[[34], [34]\]'))
+
+    def test_mismatch_and_too_many_matchers(self):
+        self.assertMismatchWithDescriptionMatching(
+            [2, 3], MatchesSetwise(Equals(0), Equals(1), Equals(2)),
+            MatchesRegex(
+                '.*There was 1 mismatch and 1 extra matcher: Equals\([01]\)',
+                re.S))
+
+    def test_mismatch_and_too_many_values(self):
+        self.assertMismatchWithDescriptionMatching(
+            [2, 3, 4], MatchesSetwise(Equals(1), Equals(2)),
+            MatchesRegex(
+                '.*There was 1 mismatch and 1 extra value: \[[34]\]',
+                re.S))
+
+    def test_mismatch_and_two_too_many_matchers(self):
+        self.assertMismatchWithDescriptionMatching(
+            [3, 4], MatchesSetwise(
+                Equals(0), Equals(1), Equals(2), Equals(3)),
+            MatchesRegex(
+                '.*There was 1 mismatch and 2 extra matchers: '
+                'Equals\([012]\), Equals\([012]\)', re.S))
+
+    def test_mismatch_and_two_too_many_values(self):
+        self.assertMismatchWithDescriptionMatching(
+            [2, 3, 4, 5], MatchesSetwise(Equals(1), Equals(2)),
+            MatchesRegex(
+                '.*There was 1 mismatch and 2 extra values: \[[145], [145]\]',
+                re.S))
+
+
+class TestContainsAllInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = ContainsAll(['foo', 'bar'])
+    matches_matches = [['foo', 'bar'], ['foo', 'z', 'bar'], ['bar', 'foo']]
+    matches_mismatches = [['f', 'g'], ['foo', 'baz'], []]
+
+    str_examples = [(
+        "MatchesAll(Contains('foo'), Contains('bar'))",
+        ContainsAll(['foo', 'bar'])),
+        ]
+
+    describe_examples = [("""Differences: [
+'baz' not in 'foo'
+]""",
+    'foo', ContainsAll(['foo', 'baz']))]
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)

=== added file 'testtools/tests/matchers/test_dict.py'
--- testtools/tests/matchers/test_dict.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/test_dict.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,217 @@
+from testtools import TestCase
+from testtools.matchers import (
+    Equals,
+    NotEquals,
+    Not,
+    )
+from testtools.matchers._dict import (
+    ContainedByDict,
+    ContainsDict,
+    KeysEqual,
+    MatchesAllDict,
+    MatchesDict,
+    _SubDictOf,
+    )
+from testtools.tests.matchers.helpers import TestMatchersInterface
+
+
+class TestMatchesAllDictInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)})
+    matches_matches = [3, 4]
+    matches_mismatches = [1, 2]
+
+    str_examples = [
+        ("MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)})",
+         matches_matcher)]
+
+    describe_examples = [
+        ("""a: 1 == 1""", 1, matches_matcher),
+        ]
+
+
+class TestKeysEqual(TestCase, TestMatchersInterface):
+
+    matches_matcher = KeysEqual('foo', 'bar')
+    matches_matches = [
+        {'foo': 0, 'bar': 1},
+        ]
+    matches_mismatches = [
+        {},
+        {'foo': 0},
+        {'bar': 1},
+        {'foo': 0, 'bar': 1, 'baz': 2},
+        {'a': None, 'b': None, 'c': None},
+        ]
+
+    str_examples = [
+        ("KeysEqual('foo', 'bar')", KeysEqual('foo', 'bar')),
+        ]
+
+    describe_examples = [
+        ("['bar', 'foo'] does not match {'baz': 2, 'foo': 0, 'bar': 1}: "
+         "Keys not equal",
+         {'foo': 0, 'bar': 1, 'baz': 2}, KeysEqual('foo', 'bar')),
+        ]
+
+
+class TestSubDictOf(TestCase, TestMatchersInterface):
+
+    matches_matcher = _SubDictOf({'foo': 'bar', 'baz': 'qux'})
+
+    matches_matches = [
+        {'foo': 'bar', 'baz': 'qux'},
+        {'foo': 'bar'},
+        ]
+
+    matches_mismatches = [
+        {'foo': 'bar', 'baz': 'qux', 'cat': 'dog'},
+        {'foo': 'bar', 'cat': 'dog'},
+        ]
+
+    str_examples = []
+    describe_examples = []
+
+
+class TestMatchesDict(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesDict(
+        {'foo': Equals('bar'), 'baz': Not(Equals('qux'))})
+
+    matches_matches = [
+        {'foo': 'bar', 'baz': None},
+        {'foo': 'bar', 'baz': 'quux'},
+        ]
+    matches_mismatches = [
+        {},
+        {'foo': 'bar', 'baz': 'qux'},
+        {'foo': 'bop', 'baz': 'qux'},
+        {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'},
+        {'foo': 'bar', 'cat': 'dog'},
+        ]
+
+    str_examples = [
+        ("MatchesDict({'foo': %s, 'baz': %s})" % (
+                Equals('bar'), Not(Equals('qux'))),
+         matches_matcher),
+        ]
+
+    describe_examples = [
+        ("Missing: {\n"
+         "  'baz': Not(Equals('qux')),\n"
+         "  'foo': Equals('bar'),\n"
+         "}",
+         {}, matches_matcher),
+        ("Differences: {\n"
+         "  'baz': 'qux' matches Equals('qux'),\n"
+         "}",
+         {'foo': 'bar', 'baz': 'qux'}, matches_matcher),
+        ("Differences: {\n"
+         "  'baz': 'qux' matches Equals('qux'),\n"
+         "  'foo': 'bar' != 'bop',\n"
+         "}",
+         {'foo': 'bop', 'baz': 'qux'}, matches_matcher),
+        ("Extra: {\n"
+         "  'cat': 'dog',\n"
+         "}",
+         {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'}, matches_matcher),
+        ("Extra: {\n"
+         "  'cat': 'dog',\n"
+         "}\n"
+         "Missing: {\n"
+         "  'baz': Not(Equals('qux')),\n"
+         "}",
+         {'foo': 'bar', 'cat': 'dog'}, matches_matcher),
+        ]
+
+
+class TestContainsDict(TestCase, TestMatchersInterface):
+
+    matches_matcher = ContainsDict(
+        {'foo': Equals('bar'), 'baz': Not(Equals('qux'))})
+
+    matches_matches = [
+        {'foo': 'bar', 'baz': None},
+        {'foo': 'bar', 'baz': 'quux'},
+        {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'},
+        ]
+    matches_mismatches = [
+        {},
+        {'foo': 'bar', 'baz': 'qux'},
+        {'foo': 'bop', 'baz': 'qux'},
+        {'foo': 'bar', 'cat': 'dog'},
+        {'foo': 'bar'},
+        ]
+
+    str_examples = [
+        ("ContainsDict({'foo': %s, 'baz': %s})" % (
+                Equals('bar'), Not(Equals('qux'))),
+         matches_matcher),
+        ]
+
+    describe_examples = [
+        ("Missing: {\n"
+         "  'baz': Not(Equals('qux')),\n"
+         "  'foo': Equals('bar'),\n"
+         "}",
+         {}, matches_matcher),
+        ("Differences: {\n"
+         "  'baz': 'qux' matches Equals('qux'),\n"
+         "}",
+         {'foo': 'bar', 'baz': 'qux'}, matches_matcher),
+        ("Differences: {\n"
+         "  'baz': 'qux' matches Equals('qux'),\n"
+         "  'foo': 'bar' != 'bop',\n"
+         "}",
+         {'foo': 'bop', 'baz': 'qux'}, matches_matcher),
+        ("Missing: {\n"
+         "  'baz': Not(Equals('qux')),\n"
+         "}",
+         {'foo': 'bar', 'cat': 'dog'}, matches_matcher),
+        ]
+
+
+class TestContainedByDict(TestCase, TestMatchersInterface):
+
+    matches_matcher = ContainedByDict(
+        {'foo': Equals('bar'), 'baz': Not(Equals('qux'))})
+
+    matches_matches = [
+        {},
+        {'foo': 'bar'},
+        {'foo': 'bar', 'baz': 'quux'},
+        {'baz': 'quux'},
+        ]
+    matches_mismatches = [
+        {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'},
+        {'foo': 'bar', 'baz': 'qux'},
+        {'foo': 'bop', 'baz': 'qux'},
+        {'foo': 'bar', 'cat': 'dog'},
+        ]
+
+    str_examples = [
+        ("ContainedByDict({'foo': %s, 'baz': %s})" % (
+                Equals('bar'), Not(Equals('qux'))),
+         matches_matcher),
+        ]
+
+    describe_examples = [
+        ("Differences: {\n"
+         "  'baz': 'qux' matches Equals('qux'),\n"
+         "}",
+         {'foo': 'bar', 'baz': 'qux'}, matches_matcher),
+        ("Differences: {\n"
+         "  'baz': 'qux' matches Equals('qux'),\n"
+         "  'foo': 'bar' != 'bop',\n"
+         "}",
+         {'foo': 'bop', 'baz': 'qux'}, matches_matcher),
+        ("Extra: {\n"
+         "  'cat': 'dog',\n"
+         "}",
+         {'foo': 'bar', 'cat': 'dog'}, matches_matcher),
+        ]
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)

=== added file 'testtools/tests/matchers/test_doctest.py'
--- testtools/tests/matchers/test_doctest.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/test_doctest.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,82 @@
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
+
+import doctest
+
+from testtools import TestCase
+from testtools.compat import (
+    str_is_unicode,
+    _b,
+    _u,
+    )
+from testtools.matchers._doctest import DocTestMatches
+from testtools.tests.helpers import FullStackRunTest
+from testtools.tests.matchers.helpers import TestMatchersInterface
+
+
+
+class TestDocTestMatchesInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = DocTestMatches("Ran 1 test in ...s", doctest.ELLIPSIS)
+    matches_matches = ["Ran 1 test in 0.000s", "Ran 1 test in 1.234s"]
+    matches_mismatches = ["Ran 1 tests in 0.000s", "Ran 2 test in 0.000s"]
+
+    str_examples = [("DocTestMatches('Ran 1 test in ...s\\n')",
+        DocTestMatches("Ran 1 test in ...s")),
+        ("DocTestMatches('foo\\n', flags=8)", DocTestMatches("foo", flags=8)),
+        ]
+
+    describe_examples = [('Expected:\n    Ran 1 tests in ...s\nGot:\n'
+        '    Ran 1 test in 0.123s\n', "Ran 1 test in 0.123s",
+        DocTestMatches("Ran 1 tests in ...s", doctest.ELLIPSIS))]
+
+
+class TestDocTestMatchesInterfaceUnicode(TestCase, TestMatchersInterface):
+
+    matches_matcher = DocTestMatches(_u("\xa7..."), doctest.ELLIPSIS)
+    matches_matches = [_u("\xa7"), _u("\xa7 more\n")]
+    matches_mismatches = ["\\xa7", _u("more \xa7"), _u("\n\xa7")]
+
+    str_examples = [("DocTestMatches(%r)" % (_u("\xa7\n"),),
+        DocTestMatches(_u("\xa7"))),
+        ]
+
+    describe_examples = [(
+        _u("Expected:\n    \xa7\nGot:\n    a\n"),
+        "a",
+        DocTestMatches(_u("\xa7"), doctest.ELLIPSIS))]
+
+
+class TestDocTestMatchesSpecific(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test___init__simple(self):
+        matcher = DocTestMatches("foo")
+        self.assertEqual("foo\n", matcher.want)
+
+    def test___init__flags(self):
+        matcher = DocTestMatches("bar\n", doctest.ELLIPSIS)
+        self.assertEqual("bar\n", matcher.want)
+        self.assertEqual(doctest.ELLIPSIS, matcher.flags)
+
+    def test_describe_non_ascii_bytes(self):
+        """Even with bytestrings, the mismatch should be coercible to unicode
+
+        DocTestMatches is intended for text, but the Python 2 str type also
+        permits arbitrary binary inputs. This is a slightly bogus thing to do,
+        and under Python 3 using bytes objects will reasonably raise an error.
+        """
+        header = _b("\x89PNG\r\n\x1a\n...")
+        if str_is_unicode:
+            self.assertRaises(TypeError,
+                DocTestMatches, header, doctest.ELLIPSIS)
+            return
+        matcher = DocTestMatches(header, doctest.ELLIPSIS)
+        mismatch = matcher.match(_b("GIF89a\1\0\1\0\0\0\0;"))
+        # Must be treatable as unicode text, the exact output matters less
+        self.assertTrue(unicode(mismatch.describe()))
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)

=== added file 'testtools/tests/matchers/test_exception.py'
--- testtools/tests/matchers/test_exception.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/test_exception.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,192 @@
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
+
+import sys
+
+from testtools import TestCase
+from testtools.matchers import (
+    AfterPreprocessing,
+    Equals,
+    )
+from testtools.matchers._exception import (
+    MatchesException,
+    Raises,
+    raises,
+    )
+from testtools.tests.helpers import FullStackRunTest
+from testtools.tests.matchers.helpers import TestMatchersInterface
+
+
+def make_error(type, *args, **kwargs):
+    try:
+        raise type(*args, **kwargs)
+    except type:
+        return sys.exc_info()
+
+
+class TestMatchesExceptionInstanceInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesException(ValueError("foo"))
+    error_foo = make_error(ValueError, 'foo')
+    error_bar = make_error(ValueError, 'bar')
+    error_base_foo = make_error(Exception, 'foo')
+    matches_matches = [error_foo]
+    matches_mismatches = [error_bar, error_base_foo]
+
+    str_examples = [
+        ("MatchesException(Exception('foo',))",
+         MatchesException(Exception('foo')))
+        ]
+    describe_examples = [
+        ("%r is not a %r" % (Exception, ValueError),
+         error_base_foo,
+         MatchesException(ValueError("foo"))),
+        ("ValueError('bar',) has different arguments to ValueError('foo',).",
+         error_bar,
+         MatchesException(ValueError("foo"))),
+        ]
+
+
+class TestMatchesExceptionTypeInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesException(ValueError)
+    error_foo = make_error(ValueError, 'foo')
+    error_sub = make_error(UnicodeError, 'bar')
+    error_base_foo = make_error(Exception, 'foo')
+    matches_matches = [error_foo, error_sub]
+    matches_mismatches = [error_base_foo]
+
+    str_examples = [
+        ("MatchesException(%r)" % Exception,
+         MatchesException(Exception))
+        ]
+    describe_examples = [
+        ("%r is not a %r" % (Exception, ValueError),
+         error_base_foo,
+         MatchesException(ValueError)),
+        ]
+
+
+class TestMatchesExceptionTypeReInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesException(ValueError, 'fo.')
+    error_foo = make_error(ValueError, 'foo')
+    error_sub = make_error(UnicodeError, 'foo')
+    error_bar = make_error(ValueError, 'bar')
+    matches_matches = [error_foo, error_sub]
+    matches_mismatches = [error_bar]
+
+    str_examples = [
+        ("MatchesException(%r)" % Exception,
+         MatchesException(Exception, 'fo.'))
+        ]
+    describe_examples = [
+        ("'bar' does not match /fo./",
+         error_bar, MatchesException(ValueError, "fo.")),
+        ]
+
+
+class TestMatchesExceptionTypeMatcherInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesException(
+        ValueError, AfterPreprocessing(str, Equals('foo')))
+    error_foo = make_error(ValueError, 'foo')
+    error_sub = make_error(UnicodeError, 'foo')
+    error_bar = make_error(ValueError, 'bar')
+    matches_matches = [error_foo, error_sub]
+    matches_mismatches = [error_bar]
+
+    str_examples = [
+        ("MatchesException(%r)" % Exception,
+         MatchesException(Exception, Equals('foo')))
+        ]
+    describe_examples = [
+        ("5 != %r" % (error_bar[1],),
+         error_bar, MatchesException(ValueError, Equals(5))),
+        ]
+
+
+class TestRaisesInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = Raises()
+    def boom():
+        raise Exception('foo')
+    matches_matches = [boom]
+    matches_mismatches = [lambda:None]
+
+    # Tricky to get function objects to render constantly, and the interfaces
+    # helper uses assertEqual rather than (for instance) DocTestMatches.
+    str_examples = []
+
+    describe_examples = []
+
+
+class TestRaisesExceptionMatcherInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = Raises(
+        exception_matcher=MatchesException(Exception('foo')))
+    def boom_bar():
+        raise Exception('bar')
+    def boom_foo():
+        raise Exception('foo')
+    matches_matches = [boom_foo]
+    matches_mismatches = [lambda:None, boom_bar]
+
+    # Tricky to get function objects to render constantly, and the interfaces
+    # helper uses assertEqual rather than (for instance) DocTestMatches.
+    str_examples = []
+
+    describe_examples = []
+
+
+class TestRaisesBaseTypes(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def raiser(self):
+        raise KeyboardInterrupt('foo')
+
+    def test_KeyboardInterrupt_matched(self):
+        # When KeyboardInterrupt is matched, it is swallowed.
+        matcher = Raises(MatchesException(KeyboardInterrupt))
+        self.assertThat(self.raiser, matcher)
+
+    def test_KeyboardInterrupt_propogates(self):
+        # The default 'it raised' propogates KeyboardInterrupt.
+        match_keyb = Raises(MatchesException(KeyboardInterrupt))
+        def raise_keyb_from_match():
+            matcher = Raises()
+            matcher.match(self.raiser)
+        self.assertThat(raise_keyb_from_match, match_keyb)
+
+    def test_KeyboardInterrupt_match_Exception_propogates(self):
+        # If the raised exception isn't matched, and it is not a subclass of
+        # Exception, it is propogated.
+        match_keyb = Raises(MatchesException(KeyboardInterrupt))
+        def raise_keyb_from_match():
+            if sys.version_info > (2, 5):
+                matcher = Raises(MatchesException(Exception))
+            else:
+                # On Python 2.4 KeyboardInterrupt is a StandardError subclass
+                # but should propogate from less generic exception matchers
+                matcher = Raises(MatchesException(EnvironmentError))
+            matcher.match(self.raiser)
+        self.assertThat(raise_keyb_from_match, match_keyb)
+
+
+class TestRaisesConvenience(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test_exc_type(self):
+        self.assertThat(lambda: 1/0, raises(ZeroDivisionError))
+
+    def test_exc_value(self):
+        e = RuntimeError("You lose!")
+        def raiser():
+            raise e
+        self.assertThat(raiser, raises(e))
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)

=== added file 'testtools/tests/matchers/test_filesystem.py'
--- testtools/tests/matchers/test_filesystem.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/test_filesystem.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,243 @@
+# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
+
+import os
+import shutil
+import tarfile
+import tempfile
+
+from testtools import TestCase
+from testtools.matchers import (
+    Contains,
+    DocTestMatches,
+    Equals,
+    )
+from testtools.matchers._filesystem import (
+    DirContains,
+    DirExists,
+    FileContains,
+    FileExists,
+    HasPermissions,
+    PathExists,
+    SamePath,
+    TarballContains,
+    )
+
+
+class PathHelpers(object):
+
+    def mkdtemp(self):
+        directory = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, directory)
+        return directory
+
+    def create_file(self, filename, contents=''):
+        fp = open(filename, 'w')
+        try:
+            fp.write(contents)
+        finally:
+            fp.close()
+
+    def touch(self, filename):
+        return self.create_file(filename)
+
+
+class TestPathExists(TestCase, PathHelpers):
+
+    def test_exists(self):
+        tempdir = self.mkdtemp()
+        self.assertThat(tempdir, PathExists())
+
+    def test_not_exists(self):
+        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
+        mismatch = PathExists().match(doesntexist)
+        self.assertThat(
+            "%s does not exist." % doesntexist, Equals(mismatch.describe()))
+
+
+class TestDirExists(TestCase, PathHelpers):
+
+    def test_exists(self):
+        tempdir = self.mkdtemp()
+        self.assertThat(tempdir, DirExists())
+
+    def test_not_exists(self):
+        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
+        mismatch = DirExists().match(doesntexist)
+        self.assertThat(
+            PathExists().match(doesntexist).describe(),
+            Equals(mismatch.describe()))
+
+    def test_not_a_directory(self):
+        filename = os.path.join(self.mkdtemp(), 'foo')
+        self.touch(filename)
+        mismatch = DirExists().match(filename)
+        self.assertThat(
+            "%s is not a directory." % filename, Equals(mismatch.describe()))
+
+
+class TestFileExists(TestCase, PathHelpers):
+
+    def test_exists(self):
+        tempdir = self.mkdtemp()
+        filename = os.path.join(tempdir, 'filename')
+        self.touch(filename)
+        self.assertThat(filename, FileExists())
+
+    def test_not_exists(self):
+        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
+        mismatch = FileExists().match(doesntexist)
+        self.assertThat(
+            PathExists().match(doesntexist).describe(),
+            Equals(mismatch.describe()))
+
+    def test_not_a_file(self):
+        tempdir = self.mkdtemp()
+        mismatch = FileExists().match(tempdir)
+        self.assertThat(
+            "%s is not a file." % tempdir, Equals(mismatch.describe()))
+
+
+class TestDirContains(TestCase, PathHelpers):
+
+    def test_empty(self):
+        tempdir = self.mkdtemp()
+        self.assertThat(tempdir, DirContains([]))
+
+    def test_not_exists(self):
+        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
+        mismatch = DirContains([]).match(doesntexist)
+        self.assertThat(
+            PathExists().match(doesntexist).describe(),
+            Equals(mismatch.describe()))
+
+    def test_contains_files(self):
+        tempdir = self.mkdtemp()
+        self.touch(os.path.join(tempdir, 'foo'))
+        self.touch(os.path.join(tempdir, 'bar'))
+        self.assertThat(tempdir, DirContains(['bar', 'foo']))
+
+    def test_matcher(self):
+        tempdir = self.mkdtemp()
+        self.touch(os.path.join(tempdir, 'foo'))
+        self.touch(os.path.join(tempdir, 'bar'))
+        self.assertThat(tempdir, DirContains(matcher=Contains('bar')))
+
+    def test_neither_specified(self):
+        self.assertRaises(AssertionError, DirContains)
+
+    def test_both_specified(self):
+        self.assertRaises(
+            AssertionError, DirContains, filenames=[], matcher=Contains('a'))
+
+    def test_does_not_contain_files(self):
+        tempdir = self.mkdtemp()
+        self.touch(os.path.join(tempdir, 'foo'))
+        mismatch = DirContains(['bar', 'foo']).match(tempdir)
+        self.assertThat(
+            Equals(['bar', 'foo']).match(['foo']).describe(),
+            Equals(mismatch.describe()))
+
+
+class TestFileContains(TestCase, PathHelpers):
+
+    def test_not_exists(self):
+        doesntexist = os.path.join(self.mkdtemp(), 'doesntexist')
+        mismatch = FileContains('').match(doesntexist)
+        self.assertThat(
+            PathExists().match(doesntexist).describe(),
+            Equals(mismatch.describe()))
+
+    def test_contains(self):
+        tempdir = self.mkdtemp()
+        filename = os.path.join(tempdir, 'foo')
+        self.create_file(filename, 'Hello World!')
+        self.assertThat(filename, FileContains('Hello World!'))
+
+    def test_matcher(self):
+        tempdir = self.mkdtemp()
+        filename = os.path.join(tempdir, 'foo')
+        self.create_file(filename, 'Hello World!')
+        self.assertThat(
+            filename, FileContains(matcher=DocTestMatches('Hello World!')))
+
+    def test_neither_specified(self):
+        self.assertRaises(AssertionError, FileContains)
+
+    def test_both_specified(self):
+        self.assertRaises(
+            AssertionError, FileContains, contents=[], matcher=Contains('a'))
+
+    def test_does_not_contain(self):
+        tempdir = self.mkdtemp()
+        filename = os.path.join(tempdir, 'foo')
+        self.create_file(filename, 'Goodbye Cruel World!')
+        mismatch = FileContains('Hello World!').match(filename)
+        self.assertThat(
+            Equals('Hello World!').match('Goodbye Cruel World!').describe(),
+            Equals(mismatch.describe()))
+class TestTarballContains(TestCase, PathHelpers):
+
+    def test_match(self):
+        tempdir = self.mkdtemp()
+        in_temp_dir = lambda x: os.path.join(tempdir, x)
+        self.touch(in_temp_dir('a'))
+        self.touch(in_temp_dir('b'))
+        tarball = tarfile.open(in_temp_dir('foo.tar.gz'), 'w')
+        tarball.add(in_temp_dir('a'), 'a')
+        tarball.add(in_temp_dir('b'), 'b')
+        tarball.close()
+        self.assertThat(
+            in_temp_dir('foo.tar.gz'), TarballContains(['b', 'a']))
+
+    def test_mismatch(self):
+        tempdir = self.mkdtemp()
+        in_temp_dir = lambda x: os.path.join(tempdir, x)
+        self.touch(in_temp_dir('a'))
+        self.touch(in_temp_dir('b'))
+        tarball = tarfile.open(in_temp_dir('foo.tar.gz'), 'w')
+        tarball.add(in_temp_dir('a'), 'a')
+        tarball.add(in_temp_dir('b'), 'b')
+        tarball.close()
+        mismatch = TarballContains(['d', 'c']).match(in_temp_dir('foo.tar.gz'))
+        self.assertEqual(
+            mismatch.describe(),
+            Equals(['c', 'd']).match(['a', 'b']).describe())
+
+
+class TestSamePath(TestCase, PathHelpers):
+
+    def test_same_string(self):
+        self.assertThat('foo', SamePath('foo'))
+
+    def test_relative_and_absolute(self):
+        path = 'foo'
+        abspath = os.path.abspath(path)
+        self.assertThat(path, SamePath(abspath))
+        self.assertThat(abspath, SamePath(path))
+
+    def test_real_path(self):
+        tempdir = self.mkdtemp()
+        source = os.path.join(tempdir, 'source')
+        self.touch(source)
+        target = os.path.join(tempdir, 'target')
+        try:
+            os.symlink(source, target)
+        except (AttributeError, NotImplementedError):
+            self.skip("No symlink support")
+        self.assertThat(source, SamePath(target))
+        self.assertThat(target, SamePath(source))
+
+
+class TestHasPermissions(TestCase, PathHelpers):
+
+    def test_match(self):
+        tempdir = self.mkdtemp()
+        filename = os.path.join(tempdir, 'filename')
+        self.touch(filename)
+        permissions = oct(os.stat(filename).st_mode)[-4:]
+        self.assertThat(filename, HasPermissions(permissions))
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)

=== added file 'testtools/tests/matchers/test_higherorder.py'
--- testtools/tests/matchers/test_higherorder.py	1970-01-01 00:00:00 +0000
+++ testtools/tests/matchers/test_higherorder.py	2012-09-08 17:30:27 +0000
@@ -0,0 +1,194 @@
+# Copyright (c) 2008-2011 testtools developers. See LICENSE for details.
+
+from testtools import TestCase
+from testtools.matchers import (
+    DocTestMatches,
+    Equals,
+    LessThan,
+    MatchesStructure,
+    Mismatch,
+    NotEquals,
+    )
+from testtools.matchers._higherorder import (
+    AfterPreprocessing,
+    AllMatch,
+    Annotate,
+    AnnotatedMismatch,
+    MatchesAny,
+    MatchesAll,
+    MatchesPredicate,
+    Not,
+    )
+from testtools.tests.helpers import FullStackRunTest
+from testtools.tests.matchers.helpers import TestMatchersInterface
+
+
+class TestAllMatch(TestCase, TestMatchersInterface):
+
+    matches_matcher = AllMatch(LessThan(10))
+    matches_matches = [
+        [9, 9, 9],
+        (9, 9),
+        iter([9, 9, 9, 9, 9]),
+        ]
+    matches_mismatches = [
+        [11, 9, 9],
+        iter([9, 12, 9, 11]),
+        ]
+
+    str_examples = [
+        ("AllMatch(LessThan(12))", AllMatch(LessThan(12))),
+        ]
+
+    describe_examples = [
+        ('Differences: [\n'
+         '10 is not > 11\n'
+         '10 is not > 10\n'
+         ']',
+         [11, 9, 10],
+         AllMatch(LessThan(10))),
+        ]
+
+
+class TestAfterPreprocessing(TestCase, TestMatchersInterface):
+
+    def parity(x):
+        return x % 2
+
+    matches_matcher = AfterPreprocessing(parity, Equals(1))
+    matches_matches = [3, 5]
+    matches_mismatches = [2]
+
+    str_examples = [
+        ("AfterPreprocessing(<function parity>, Equals(1))",
+         AfterPreprocessing(parity, Equals(1))),
+        ]
+
+    describe_examples = [
+        ("1 != 0: after <function parity> on 2", 2,
+         AfterPreprocessing(parity, Equals(1))),
+        ("1 != 0", 2,
+         AfterPreprocessing(parity, Equals(1), annotate=False)),
+        ]
+
+class TestMatchersAnyInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesAny(DocTestMatches("1"), DocTestMatches("2"))
+    matches_matches = ["1", "2"]
+    matches_mismatches = ["3"]
+
+    str_examples = [(
+        "MatchesAny(DocTestMatches('1\\n'), DocTestMatches('2\\n'))",
+        MatchesAny(DocTestMatches("1"), DocTestMatches("2"))),
+        ]
+
+    describe_examples = [("""Differences: [
+Expected:
+    1
+Got:
+    3
+
+Expected:
+    2
+Got:
+    3
+
+]""",
+        "3", MatchesAny(DocTestMatches("1"), DocTestMatches("2")))]
+
+
+class TestMatchesAllInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesAll(NotEquals(1), NotEquals(2))
+    matches_matches = [3, 4]
+    matches_mismatches = [1, 2]
+
+    str_examples = [
+        ("MatchesAll(NotEquals(1), NotEquals(2))",
+         MatchesAll(NotEquals(1), NotEquals(2)))]
+
+    describe_examples = [
+        ("""Differences: [
+1 == 1
+]""",
+         1, MatchesAll(NotEquals(1), NotEquals(2))),
+        ("1 == 1", 1,
+         MatchesAll(NotEquals(2), NotEquals(1), Equals(3), first_only=True)),
+        ]
+
+
+class TestAnnotate(TestCase, TestMatchersInterface):
+
+    matches_matcher = Annotate("foo", Equals(1))
+    matches_matches = [1]
+    matches_mismatches = [2]
+
+    str_examples = [
+        ("Annotate('foo', Equals(1))", Annotate("foo", Equals(1)))]
+
+    describe_examples = [("1 != 2: foo", 2, Annotate('foo', Equals(1)))]
+
+    def test_if_message_no_message(self):
+        # Annotate.if_message returns the given matcher if there is no
+        # message.
+        matcher = Equals(1)
+        not_annotated = Annotate.if_message('', matcher)
+        self.assertIs(matcher, not_annotated)
+
+    def test_if_message_given_message(self):
+        # Annotate.if_message returns an annotated version of the matcher if a
+        # message is provided.
+        matcher = Equals(1)
+        expected = Annotate('foo', matcher)
+        annotated = Annotate.if_message('foo', matcher)
+        self.assertThat(
+            annotated,
+            MatchesStructure.fromExample(expected, 'annotation', 'matcher'))
+
+
+class TestAnnotatedMismatch(TestCase):
+
+    run_tests_with = FullStackRunTest
+
+    def test_forwards_details(self):
+        x = Mismatch('description', {'foo': 'bar'})
+        annotated = AnnotatedMismatch("annotation", x)
+        self.assertEqual(x.get_details(), annotated.get_details())
+
+
+class TestNotInterface(TestCase, TestMatchersInterface):
+
+    matches_matcher = Not(Equals(1))
+    matches_matches = [2]
+    matches_mismatches = [1]
+
+    str_examples = [
+        ("Not(Equals(1))", Not(Equals(1))),
+        ("Not(Equals('1'))", Not(Equals('1')))]
+
+    describe_examples = [('1 matches Equals(1)', 1, Not(Equals(1)))]
+
+
+def is_even(x):
+    return x % 2 == 0
+
+
+class TestMatchesPredicate(TestCase, TestMatchersInterface):
+
+    matches_matcher = MatchesPredicate(is_even, "%s is not even")
+    matches_matches = [2, 4, 6, 8]
+    matches_mismatches = [3, 5, 7, 9]
+
+    str_examples = [
+        ("MatchesPredicate(%r, %r)" % (is_even, "%s is not even"),
+         MatchesPredicate(is_even, "%s is not even")),
+        ]
+
+    describe_examples = [
+        ('7 is not even', 7, MatchesPredicate(is_even, "%s is not even")),
+        ]
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)


Follow ups