~testtools-dev/testtools/trunk

171.1.1 by Jonathan Lange
Get it right.
1
# Copyright (c) 2009-2011 testtools developers. See LICENSE for details.
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
2
3
"""Matchers, a way to express complex assertions outside the testcase.
4
5
Inspired by 'hamcrest'.
6
7
Matcher provides the abstract API that all matchers need to implement.
8
9
Bundled matchers are listed in __all__: a list can be obtained by running
10
$ python -c 'import testtools.matchers; print testtools.matchers.__all__'
11
"""
12
13
__metaclass__ = type
14
__all__ = [
204.1.1 by Jonathan Lange
Rename AfterPreproccessing to AfterPreprocessing, keeping a backward compatibility shim.
15
    'AfterPreprocessing',
204.2.1 by Jonathan Lange
Add a new matcher.
16
    'AllMatch',
61.1.1 by Jonathan Lange
Add an 'Annotation' file.
17
    'Annotate',
225.1.2 by Robert Collins
Migrate assertIn to be matcher based.
18
    'Contains',
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
19
    'DirExists',
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
20
    'DocTestMatches',
164.1.1 by Jonathan Lange
Make sure all matchers are in matchers __all__
21
    'EndsWith',
33.5.2 by Robert Collins
Add an Equals matcher.
22
    'Equals',
233.2.2 by Jonathan Lange
Even more tests
23
    'FileContains',
233.2.3 by Jonathan Lange
Add FileExists and correct tests for DirExists
24
    'FileExists',
182.2.31 by Jonathan Lange
Restore matchers.
25
    'GreaterThan',
233.2.11 by Jonathan Lange
More matchers still.
26
    'HasPermissions',
87.1.4 by Jonathan Lange
Add an Is matcher
27
    'Is',
225.1.7 by Robert Collins
Add an IsInstance matcher.
28
    'IsInstance',
164.1.1 by Jonathan Lange
Make sure all matchers are in matchers __all__
29
    'KeysEqual',
91 by Robert Collins
New LessThan matcher.
30
    'LessThan',
53.3.4 by Jonathan Lange
Add a MatchesAll matcher.
31
    'MatchesAll',
33.5.1 by Robert Collins
Add a new matchers MatchesAny.
32
    'MatchesAny',
128.1.1 by Robert Collins
Add MatchesException matcher.
33
    'MatchesException',
182.2.31 by Jonathan Lange
Restore matchers.
34
    'MatchesListwise',
233.2.5 by Jonathan Lange
Add a matcher that just takes a predicate and a message.
35
    'MatchesPredicate',
182.2.31 by Jonathan Lange
Restore matchers.
36
    'MatchesRegex',
37
    'MatchesSetwise',
38
    'MatchesStructure',
53.3.4 by Jonathan Lange
Add a MatchesAll matcher.
39
    'NotEquals',
53.3.6 by Jonathan Lange
More docs on NotEquals.
40
    'Not',
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
41
    'PathExists',
128.1.2 by Robert Collins
Add a Raises matcher.
42
    'Raises',
128.1.11 by Jonathan Lange
Add 'raises'
43
    'raises',
233.2.10 by Jonathan Lange
Add SamePath
44
    'SamePath',
104 by Robert Collins
StartsWith and DoesNotStartWith matchers from Launchpad.
45
    'StartsWith',
233.2.9 by Jonathan Lange
Add tarball matcher.
46
    'TarballContains',
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
47
    ]
48
49
import doctest
87.1.2 by Jonathan Lange
Refactor to reduce boilerplate.
50
import operator
97.1.1 by Robert Collins
Code duplication between assertEqual and the matcher Equals has been removed.
51
from pprint import pformat
169.1.4 by Jonathan Lange
Add option regular expression matching logic to MatchesException.
52
import re
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
53
import os
128.1.2 by Robert Collins
Add a Raises matcher.
54
import sys
233.2.9 by Jonathan Lange
Add tarball matcher.
55
import tarfile
155.1.7 by Michael Hudson
add AfterPreprocessing
56
import types
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
57
182.2.7 by Jonathan Lange
Make mismatches between big blocks of text easier to read.
58
from testtools.compat import (
59
    classtypes,
60
    _error_repr,
61
    isbaseexception,
219.4.17 by Martin
Hack over bump with bytes till things can be rewritten in a better way
62
    _isbytes,
182.2.7 by Jonathan Lange
Make mismatches between big blocks of text easier to read.
63
    istext,
219.4.14 by Martin
Make MismatchError stringification return appropriate types on Python 2
64
    str_is_unicode,
219.4.13 by Martin
Use (possibly extended) repr rather than "%s" for matchee in verbose form of MismatchError
65
    text_repr
182.2.7 by Jonathan Lange
Make mismatches between big blocks of text easier to read.
66
    )
130.1.3 by Martin
Adapt MatchesException to work with old-style Exception classes in Python 2.4
67
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
68
80.2.1 by Jamu Kakar
- All old-style classes are now new-style classes.
69
class Matcher(object):
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
70
    """A pattern matcher.
71
61.1.4 by Jonathan Lange
Revert the matcher change.
72
    A Matcher must implement match and __str__ to be used by
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
73
    testtools.TestCase.assertThat. Matcher.match(thing) returns None when
74
    thing is completely matched, and a Mismatch object otherwise.
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
75
32.1.4 by Jonathan Lange
Apply review feedback.
76
    Matchers can be useful outside of test cases, as they are simply a
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
77
    pattern matching language expressed as objects.
78
79
    testtools.matchers is inspired by hamcrest, but is pythonic rather than
80
    a Java transcription.
81
    """
82
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
83
    def match(self, something):
84
        """Return None if this matcher matches something, a Mismatch otherwise.
85
        """
86
        raise NotImplementedError(self.match)
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
87
61.1.4 by Jonathan Lange
Revert the matcher change.
88
    def __str__(self):
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
89
        """Get a sensible human representation of the matcher.
32.1.4 by Jonathan Lange
Apply review feedback.
90
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
91
        This should include the parameters given to the matcher and any
92
        state that would affect the matches operation.
93
        """
61.1.4 by Jonathan Lange
Revert the matcher change.
94
        raise NotImplementedError(self.__str__)
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
95
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
96
80.2.1 by Jamu Kakar
- All old-style classes are now new-style classes.
97
class Mismatch(object):
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
98
    """An object describing a mismatch detected by a Matcher."""
99
93.1.1 by Jonathan Lange
Add optional parameters to Mismatch.
100
    def __init__(self, description=None, details=None):
101
        """Construct a `Mismatch`.
102
103
        :param description: A description to use.  If not provided,
104
            `Mismatch.describe` must be implemented.
105
        :param details: Extra details about the mismatch.  Defaults
106
            to the empty dict.
107
        """
108
        if description:
109
            self._description = description
110
        if details is None:
111
            details = {}
112
        self._details = details
113
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
114
    def describe(self):
115
        """Describe the mismatch.
32.1.4 by Jonathan Lange
Apply review feedback.
116
117
        This should be either a human-readable string or castable to a string.
219.4.24 by Martin
Add cautions about unprintable strings to Mismatch documentation
118
        In particular, is should either be plain ascii or unicode on Python 2,
119
        and care should be taken to escape control characters.
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
120
        """
93.1.1 by Jonathan Lange
Add optional parameters to Mismatch.
121
        try:
122
            return self._description
123
        except AttributeError:
124
            raise NotImplementedError(self.describe)
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
125
66.3.3 by James Westby
Fixes from Rob's review, thanks.
126
    def get_details(self):
66.3.2 by James Westby
Add a getDetails that returns [] to the Mismatch class.
127
        """Get extra details about the mismatch.
128
71 by Robert Collins
* Thanks to a patch from James Westby testtools.matchers.Mismatch can now
129
        This allows the mismatch to provide extra information beyond the basic
130
        description, including large text or binary files, or debugging internals
131
        without having to force it to fit in the output of 'describe'.
132
133
        The testtools assertion assertThat will query get_details and attach
134
        all its values to the test, permitting them to be reported in whatever
135
        manner the test environment chooses.
136
137
        :return: a dict mapping names to Content objects. name is a string to
66.3.4 by James Westby
Switch get_details to return a dict.
138
            name the detail, and the Content object is the detail to add
71 by Robert Collins
* Thanks to a patch from James Westby testtools.matchers.Mismatch can now
139
            to the result. For more information see the API to which items from
140
            this dict are passed testtools.TestCase.addDetail.
66.3.2 by James Westby
Add a getDetails that returns [] to the Mismatch class.
141
        """
93.1.1 by Jonathan Lange
Add optional parameters to Mismatch.
142
        return getattr(self, '_details', {})
66.3.2 by James Westby
Add a getDetails that returns [] to the Mismatch class.
143
128.1.2 by Robert Collins
Add a Raises matcher.
144
    def __repr__(self):
145
        return  "<testtools.matchers.Mismatch object at %x attributes=%r>" % (
146
            id(self), self.__dict__)
147
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
148
219.4.3 by Jonathan Lange
Add MismatchError, with tests. Change assertThat to raise MismatchError
149
class MismatchError(AssertionError):
150
    """Raised when a mismatch occurs."""
151
152
    # This class exists to work around
153
    # <https://bugs.launchpad.net/testtools/+bug/804127>.  It provides a
154
    # guaranteed way of getting a readable exception, no matter what crazy
155
    # characters are in the matchee, matcher or mismatch.
156
157
    def __init__(self, matchee, matcher, mismatch, verbose=False):
158
        # Have to use old-style upcalling for Python 2.4 and 2.5
159
        # compatibility.
160
        AssertionError.__init__(self)
161
        self.matchee = matchee
162
        self.matcher = matcher
163
        self.mismatch = mismatch
164
        self.verbose = verbose
165
166
    def __str__(self):
167
        difference = self.mismatch.describe()
168
        if self.verbose:
219.4.17 by Martin
Hack over bump with bytes till things can be rewritten in a better way
169
            # GZ 2011-08-24: Smelly API? Better to take any object and special
170
            #                case text inside?
171
            if istext(self.matchee) or _isbytes(self.matchee):
219.4.25 by Martin
Add third state to multiline argument of text_repr and comment the internal logic
172
                matchee = text_repr(self.matchee, multiline=False)
219.4.13 by Martin
Use (possibly extended) repr rather than "%s" for matchee in verbose form of MismatchError
173
            else:
174
                matchee = repr(self.matchee)
219.4.3 by Jonathan Lange
Add MismatchError, with tests. Change assertThat to raise MismatchError
175
            return (
219.4.13 by Martin
Use (possibly extended) repr rather than "%s" for matchee in verbose form of MismatchError
176
                'Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n'
177
                % (matchee, self.matcher, difference))
219.4.3 by Jonathan Lange
Add MismatchError, with tests. Change assertThat to raise MismatchError
178
        else:
179
            return difference
180
219.4.14 by Martin
Make MismatchError stringification return appropriate types on Python 2
181
    if not str_is_unicode:
182
183
        __unicode__ = __str__
184
185
        def __str__(self):
186
            return self.__unicode__().encode("ascii", "backslashreplace")
187
219.4.3 by Jonathan Lange
Add MismatchError, with tests. Change assertThat to raise MismatchError
188
179.1.2 by Jonathan Lange
Add a MismatchDecorator.
189
class MismatchDecorator(object):
179.1.3 by Jonathan Lange
Docs!
190
    """Decorate a ``Mismatch``.
191
192
    Forwards all messages to the original mismatch object.  Probably the best
193
    way to use this is inherit from this class and then provide your own
194
    custom decoration logic.
195
    """
179.1.2 by Jonathan Lange
Add a MismatchDecorator.
196
197
    def __init__(self, original):
179.1.3 by Jonathan Lange
Docs!
198
        """Construct a `MismatchDecorator`.
199
200
        :param original: A `Mismatch` object to decorate.
201
        """
179.1.2 by Jonathan Lange
Add a MismatchDecorator.
202
        self.original = original
203
179.1.4 by Jonathan Lange
Use the decorator. Give the decorator a repr
204
    def __repr__(self):
205
        return '<testtools.matchers.MismatchDecorator(%r)>' % (self.original,)
206
179.1.2 by Jonathan Lange
Add a MismatchDecorator.
207
    def describe(self):
208
        return self.original.describe()
209
210
    def get_details(self):
211
        return self.original.get_details()
212
213
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
214
class _NonManglingOutputChecker(doctest.OutputChecker):
215
    """Doctest checker that works with unicode rather than mangling strings
216
217
    This is needed because current Python versions have tried to fix string
233.2.14 by Jonathan Lange
More doc tweaks
218
    encoding related problems, but regressed the default behaviour with
219
    unicode inputs in the process.
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
220
233.2.14 by Jonathan Lange
More doc tweaks
221
    In Python 2.6 and 2.7 ``OutputChecker.output_difference`` is was changed
222
    to return a bytestring encoded as per ``sys.stdout.encoding``, or utf-8 if
223
    that can't be determined. Worse, that encoding process happens in the
224
    innocent looking `_indent` global function. Because the
225
    `DocTestMismatch.describe` result may well not be destined for printing to
226
    stdout, this is no good for us. To get a unicode return as before, the
227
    method is monkey patched if ``doctest._encoding`` exists.
192.3.5 by Martin
Trailing whitespace nit
228
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
229
    Python 3 has a different problem. For some reason both inputs are encoded
230
    to ascii with 'backslashreplace', making an escaped string matches its
233.2.14 by Jonathan Lange
More doc tweaks
231
    unescaped form. Overriding the offending ``OutputChecker._toAscii`` method
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
232
    is sufficient to revert this.
233
    """
192.3.5 by Martin
Trailing whitespace nit
234
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
235
    def _toAscii(self, s):
233.2.14 by Jonathan Lange
More doc tweaks
236
        """Return ``s`` unchanged rather than mangling it to ascii"""
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
237
        return s
192.3.5 by Martin
Trailing whitespace nit
238
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
239
    # Only do this overriding hackery if doctest has a broken _input function
240
    if getattr(doctest, "_encoding", None) is not None:
241
        from types import FunctionType as __F
242
        __f = doctest.OutputChecker.output_difference.im_func
243
        __g = dict(__f.func_globals)
244
        def _indent(s, indent=4, _pattern=re.compile("^(?!$)", re.MULTILINE)):
233.2.14 by Jonathan Lange
More doc tweaks
245
            """Prepend non-empty lines in ``s`` with ``indent`` number of spaces"""
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
246
            return _pattern.sub(indent*" ", s)
247
        __g["_indent"] = _indent
248
        output_difference = __F(__f.func_code, __g, "output_difference")
249
        del __F, __f, __g, _indent
250
251
80.2.1 by Jamu Kakar
- All old-style classes are now new-style classes.
252
class DocTestMatches(object):
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
253
    """See if a string matches a doctest example."""
254
255
    def __init__(self, example, flags=0):
256
        """Create a DocTestMatches to match example.
257
258
        :param example: The example to match e.g. 'foo bar baz'
259
        :param flags: doctest comparison flags to match on. e.g.
260
            doctest.ELLIPSIS.
261
        """
262
        if not example.endswith('\n'):
263
            example += '\n'
264
        self.want = example # required variable name by doctest.
265
        self.flags = flags
192.3.4 by Martin
Implement _NonManglingOutputChecker class to work around doctest problems in recent Python versions
266
        self._checker = _NonManglingOutputChecker()
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
267
61.1.4 by Jonathan Lange
Revert the matcher change.
268
    def __str__(self):
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
269
        if self.flags:
270
            flagstr = ", flags=%d" % self.flags
271
        else:
272
            flagstr = ""
273
        return 'DocTestMatches(%r%s)' % (self.want, flagstr)
274
275
    def _with_nl(self, actual):
192.3.1 by Martin
Incomplete fix for using unicode with DocTestMatches, needs doctest._indent issue fixing still
276
        result = self.want.__class__(actual)
27.1.2 by Robert Collins
Implement a built in DocTestMatches matcher.
277
        if not result.endswith('\n'):
278
            result += '\n'
279
        return result
280
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
281
    def match(self, actual):
282
        with_nl = self._with_nl(actual)
283
        if self._checker.check_output(self.want, with_nl, self.flags):
284
            return None
32.1.2 by Robert Collins
Clarity tweaks from Martin.
285
        return DocTestMismatch(self, with_nl)
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
286
287
    def _describe_difference(self, with_nl):
288
        return self._checker.output_difference(self, with_nl, self.flags)
289
290
87.1.5 by Jonathan Lange
I can't believe we let this happen.
291
class DocTestMismatch(Mismatch):
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
292
    """Mismatch object for DocTestMatches."""
32.1.4 by Jonathan Lange
Apply review feedback.
293
32.1.1 by Robert Collins
Change the Matcher API to be robust even if matching were to have side effects.
294
    def __init__(self, matcher, with_nl):
295
        self.matcher = matcher
296
        self.with_nl = with_nl
297
298
    def describe(self):
219.4.21 by Martin
Avoid potential bytes and unicode mixing with DocTestMismatch on Python 2
299
        s = self.matcher._describe_difference(self.with_nl)
300
        if str_is_unicode or isinstance(s, unicode):
301
            return s
302
        # GZ 2011-08-24: This is actually pretty bogus, most C0 codes should
303
        #                be escaped, in addition to non-ascii bytes.
304
        return s.decode("latin1").encode("ascii", "backslashreplace")
33.5.1 by Robert Collins
Add a new matchers MatchesAny.
305
306
225.1.1 by Robert Collins
New matcher Contains.
307
class DoesNotContain(Mismatch):
308
309
    def __init__(self, matchee, needle):
310
        """Create a DoesNotContain Mismatch.
311
312
        :param matchee: the object that did not contain needle.
313
        :param needle: the needle that 'matchee' was expected to contain.
314
        """
315
        self.matchee = matchee
316
        self.needle = needle
317
318
    def describe(self):
225.1.11 by Jonathan Lange
Remove the word 'present', which is unnecessary.
319
        return "%r not in %r" % (self.needle, self.matchee)
225.1.1 by Robert Collins
New matcher Contains.
320
321
104 by Robert Collins
StartsWith and DoesNotStartWith matchers from Launchpad.
322
class DoesNotStartWith(Mismatch):
323
324
    def __init__(self, matchee, expected):
325
        """Create a DoesNotStartWith Mismatch.
326
327
        :param matchee: the string that did not match.
168.1.2 by Jonathan Lange
Fix some epytext failures.
328
        :param expected: the string that 'matchee' was expected to start with.
104 by Robert Collins
StartsWith and DoesNotStartWith matchers from Launchpad.
329
        """
330
        self.matchee = matchee
331
        self.expected = expected
332
333
    def describe(self):
219.4.18 by Martin
Use text_repr for DoesNotStartWith and DoesNotEndWith describe methods
334
        return "%s does not start with %s." % (
335
            text_repr(self.matchee), text_repr(self.expected))
104 by Robert Collins
StartsWith and DoesNotStartWith matchers from Launchpad.
336
337
138.1.1 by Jonathan Lange
EndsWith matcher.
338
class DoesNotEndWith(Mismatch):
339
340
    def __init__(self, matchee, expected):
341
        """Create a DoesNotEndWith Mismatch.
342
343
        :param matchee: the string that did not match.
168.1.2 by Jonathan Lange
Fix some epytext failures.
344
        :param expected: the string that 'matchee' was expected to end with.
138.1.1 by Jonathan Lange
EndsWith matcher.
345
        """
346
        self.matchee = matchee
347
        self.expected = expected
348
349
    def describe(self):
219.4.18 by Martin
Use text_repr for DoesNotStartWith and DoesNotEndWith describe methods
350
        return "%s does not end with %s." % (
351
            text_repr(self.matchee), text_repr(self.expected))
138.1.1 by Jonathan Lange
EndsWith matcher.
352
353
87.1.2 by Jonathan Lange
Refactor to reduce boilerplate.
354
class _BinaryComparison(object):
355
    """Matcher that compares an object to another object."""
33.5.2 by Robert Collins
Add an Equals matcher.
356
357
    def __init__(self, expected):
358
        self.expected = expected
359
87.1.2 by Jonathan Lange
Refactor to reduce boilerplate.
360
    def __str__(self):
361
        return "%s(%r)" % (self.__class__.__name__, self.expected)
362
33.5.2 by Robert Collins
Add an Equals matcher.
363
    def match(self, other):
91 by Robert Collins
New LessThan matcher.
364
        if self.comparator(other, self.expected):
33.5.2 by Robert Collins
Add an Equals matcher.
365
            return None
87.1.3 by Jonathan Lange
Refactor some more.
366
        return _BinaryMismatch(self.expected, self.mismatch_string, other)
33.5.2 by Robert Collins
Add an Equals matcher.
367
87.1.2 by Jonathan Lange
Refactor to reduce boilerplate.
368
    def comparator(self, expected, other):
369
        raise NotImplementedError(self.comparator)
33.5.2 by Robert Collins
Add an Equals matcher.
370
371
87.1.5 by Jonathan Lange
I can't believe we let this happen.
372
class _BinaryMismatch(Mismatch):
87.1.3 by Jonathan Lange
Refactor some more.
373
    """Two things did not match."""
33.5.2 by Robert Collins
Add an Equals matcher.
374
87.1.3 by Jonathan Lange
Refactor some more.
375
    def __init__(self, expected, mismatch_string, other):
33.5.2 by Robert Collins
Add an Equals matcher.
376
        self.expected = expected
87.1.3 by Jonathan Lange
Refactor some more.
377
        self._mismatch_string = mismatch_string
33.5.2 by Robert Collins
Add an Equals matcher.
378
        self.other = other
379
182.2.29 by Jonathan Lange
Oh man I hope this works.
380
    def _format(self, thing):
381
        # Blocks of text with newlines are formatted as triple-quote
382
        # strings. Everything else is pretty-printed.
219.4.25 by Martin
Add third state to multiline argument of text_repr and comment the internal logic
383
        if istext(thing) or _isbytes(thing):
384
            return text_repr(thing)
182.2.29 by Jonathan Lange
Oh man I hope this works.
385
        return pformat(thing)
386
33.5.2 by Robert Collins
Add an Equals matcher.
387
    def describe(self):
97.1.1 by Robert Collins
Code duplication between assertEqual and the matcher Equals has been removed.
388
        left = repr(self.expected)
389
        right = repr(self.other)
390
        if len(left) + len(right) > 70:
230.1.1 by Jonathan Lange
Align actual and reference = symbols.
391
            return "%s:\nreference = %s\nactual    = %s\n" % (
182.2.29 by Jonathan Lange
Oh man I hope this works.
392
                self._mismatch_string, self._format(self.expected),
393
                self._format(self.other))
97.1.1 by Robert Collins
Code duplication between assertEqual and the matcher Equals has been removed.
394
        else:
219.4.16 by Martin
Test and fix _BinaryMismatch.describe long forms using text_repr
395
            return "%s %s %s" % (left, self._mismatch_string, right)
33.5.2 by Robert Collins
Add an Equals matcher.
396
397
233.2.5 by Jonathan Lange
Add a matcher that just takes a predicate and a message.
398
class MatchesPredicate(Matcher):
233.2.17 by Jonathan Lange
Docstrings for MatchesPredicate
399
    """Match if a given function returns True.
400
401
    It is reasonably common to want to make a very simple matcher based on a
402
    function that you already have that returns True or False given a single
403
    argument (i.e. a predicate function).  This matcher makes it very easy to
404
    do so. e.g.::
405
406
      IsEven = MatchesPredicate(lambda x: x % 2 == 0, '%s is not even')
407
      self.assertThat(4, IsEven)
408
    """
233.2.5 by Jonathan Lange
Add a matcher that just takes a predicate and a message.
409
410
    def __init__(self, predicate, message):
233.2.17 by Jonathan Lange
Docstrings for MatchesPredicate
411
        """Create a ``MatchesPredicate`` matcher.
412
413
        :param predicate: A function that takes a single argument and returns
414
            a value that will be interpreted as a boolean.
415
        :param message: A message to describe a mismatch.  It will be formatted
416
            with '%' and be given whatever was passed to ``match()``. Thus, it
417
            needs to contain exactly one thing like '%s', '%d' or '%f'.
418
        """
233.2.5 by Jonathan Lange
Add a matcher that just takes a predicate and a message.
419
        self.predicate = predicate
420
        self.message = message
421
422
    def __str__(self):
423
        return '%s(%r, %r)' % (
424
            self.__class__.__name__, self.predicate, self.message)
425
426
    def match(self, x):
427
        if not self.predicate(x):
428
            return Mismatch(self.message % x)
429
430
87.1.2 by Jonathan Lange
Refactor to reduce boilerplate.
431
class Equals(_BinaryComparison):
432
    """Matches if the items are equal."""
433
434
    comparator = operator.eq
87.1.3 by Jonathan Lange
Refactor some more.
435
    mismatch_string = '!='
87.1.2 by Jonathan Lange
Refactor to reduce boilerplate.
436
437
438
class NotEquals(_BinaryComparison):
53.3.6 by Jonathan Lange
More docs on NotEquals.
439
    """Matches if the items are not equal.
440
168.1.2 by Jonathan Lange
Fix some epytext failures.
441
    In most cases, this is equivalent to ``Not(Equals(foo))``. The difference
442
    only matters when testing ``__ne__`` implementations.
53.3.6 by Jonathan Lange
More docs on NotEquals.
443
    """
53.3.3 by Jonathan Lange
A NotEquals matcher.
444
87.1.2 by Jonathan Lange
Refactor to reduce boilerplate.
445
    comparator = operator.ne
87.1.3 by Jonathan Lange
Refactor some more.
446
    mismatch_string = '=='
53.3.3 by Jonathan Lange
A NotEquals matcher.
447
448
87.1.4 by Jonathan Lange
Add an Is matcher
449
class Is(_BinaryComparison):
450
    """Matches if the items are identical."""
451
452
    comparator = operator.is_
453
    mismatch_string = 'is not'
454
455
225.1.7 by Robert Collins
Add an IsInstance matcher.
456
class IsInstance(object):
457
    """Matcher that wraps isinstance."""
458
459
    def __init__(self, *types):
460
        self.types = tuple(types)
461
462
    def __str__(self):
463
        return "%s(%s)" % (self.__class__.__name__,
464
                ', '.join(type.__name__ for type in self.types))
465
466
    def match(self, other):
467
        if isinstance(other, self.types):
468
            return None
469
        return NotAnInstance(other, self.types)
470
471
472
class NotAnInstance(Mismatch):
473
474
    def __init__(self, matchee, types):
475
        """Create a NotAnInstance Mismatch.
476
477
        :param matchee: the thing which is not an instance of any of types.
478
        :param types: A tuple of the types which were expected.
479
        """
480
        self.matchee = matchee
481
        self.types = types
482
483
    def describe(self):
484
        if len(self.types) == 1:
485
            typestr = self.types[0].__name__
486
        else:
487
            typestr = 'any of (%s)' % ', '.join(type.__name__ for type in
488
                    self.types)
489
        return "'%s' is not an instance of %s" % (self.matchee, typestr)
490
491
91 by Robert Collins
New LessThan matcher.
492
class LessThan(_BinaryComparison):
493
    """Matches if the item is less than the matchers reference object."""
494
495
    comparator = operator.__lt__
184.1.1 by Gavin Panella
Correct the message than LessThan() provides on mismatch.
496
    mismatch_string = 'is not >'
91 by Robert Collins
New LessThan matcher.
497
192.2.2 by Jonathan Lange
Fix up whitespace problems
498
192.2.1 by Christian Kampka
* New GreaterThan matcher
499
class GreaterThan(_BinaryComparison):
500
    """Matches if the item is greater than the matchers reference object."""
501
502
    comparator = operator.__gt__
503
    mismatch_string = 'is not <'
91 by Robert Collins
New LessThan matcher.
504
192.2.2 by Jonathan Lange
Fix up whitespace problems
505
80.2.1 by Jamu Kakar
- All old-style classes are now new-style classes.
506
class MatchesAny(object):
33.5.1 by Robert Collins
Add a new matchers MatchesAny.
507
    """Matches if any of the matchers it is created with match."""
508
509
    def __init__(self, *matchers):
510
        self.matchers = matchers
511
512
    def match(self, matchee):
513
        results = []
514
        for matcher in self.matchers:
515
            mismatch = matcher.match(matchee)
516
            if mismatch is None:
517
                return None
518
            results.append(mismatch)
519
        return MismatchesAll(results)
520
61.1.4 by Jonathan Lange
Revert the matcher change.
521
    def __str__(self):
33.5.1 by Robert Collins
Add a new matchers MatchesAny.
522
        return "MatchesAny(%s)" % ', '.join([
61.1.4 by Jonathan Lange
Revert the matcher change.
523
            str(matcher) for matcher in self.matchers])
33.5.1 by Robert Collins
Add a new matchers MatchesAny.
524
525
80.2.1 by Jamu Kakar
- All old-style classes are now new-style classes.
526
class MatchesAll(object):
53.3.4 by Jonathan Lange
Add a MatchesAll matcher.
527
    """Matches if all of the matchers it is created with match."""
528
233.2.4 by Jonathan Lange
Add a short-circuit option to MatchesAll
529
    def __init__(self, *matchers, **options):
530
        """Construct a MatchesAll matcher.
531
233.2.14 by Jonathan Lange
More doc tweaks
532
        Just list the component matchers as arguments in the ``*args``
533
        style. If you want only the first mismatch to be reported, past in
233.2.4 by Jonathan Lange
Add a short-circuit option to MatchesAll
534
        first_only=True as a keyword argument. By default, all mismatches are
535
        reported.
536
        """
53.3.4 by Jonathan Lange
Add a MatchesAll matcher.
537
        self.matchers = matchers
233.2.4 by Jonathan Lange
Add a short-circuit option to MatchesAll
538
        self.first_only = options.get('first_only', False)
53.3.4 by Jonathan Lange
Add a MatchesAll matcher.
539
61.1.4 by Jonathan Lange
Revert the matcher change.
540
    def __str__(self):
541
        return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers))
53.3.4 by Jonathan Lange
Add a MatchesAll matcher.
542
543
    def match(self, matchee):
544
        results = []
545
        for matcher in self.matchers:
546
            mismatch = matcher.match(matchee)
547
            if mismatch is not None:
233.2.4 by Jonathan Lange
Add a short-circuit option to MatchesAll
548
                if self.first_only:
549
                    return mismatch
53.3.4 by Jonathan Lange
Add a MatchesAll matcher.
550
                results.append(mismatch)
551
        if results:
552
            return MismatchesAll(results)
553
        else:
554
            return None
555
556
87.1.5 by Jonathan Lange
I can't believe we let this happen.
557
class MismatchesAll(Mismatch):
33.5.1 by Robert Collins
Add a new matchers MatchesAny.
558
    """A mismatch with many child mismatches."""
559
560
    def __init__(self, mismatches):
561
        self.mismatches = mismatches
562
563
    def describe(self):
564
        descriptions = ["Differences: ["]
565
        for mismatch in self.mismatches:
566
            descriptions.append(mismatch.describe())
155.2.2 by Michael Hudson
remove the final newline from the description of MismatchesAll
567
        descriptions.append("]")
33.5.1 by Robert Collins
Add a new matchers MatchesAny.
568
        return '\n'.join(descriptions)
53.3.1 by Jonathan Lange
Add a Not matcher.
569
570
80.2.1 by Jamu Kakar
- All old-style classes are now new-style classes.
571
class Not(object):
53.3.1 by Jonathan Lange
Add a Not matcher.
572
    """Inverts a matcher."""
573
574
    def __init__(self, matcher):
575
        self.matcher = matcher
576
61.1.4 by Jonathan Lange
Revert the matcher change.
577
    def __str__(self):
578
        return 'Not(%s)' % (self.matcher,)
53.3.1 by Jonathan Lange
Add a Not matcher.
579
580
    def match(self, other):
581
        mismatch = self.matcher.match(other)
582
        if mismatch is None:
583
            return MatchedUnexpectedly(self.matcher, other)
584
        else:
585
            return None
586
587
87.1.5 by Jonathan Lange
I can't believe we let this happen.
588
class MatchedUnexpectedly(Mismatch):
53.3.1 by Jonathan Lange
Add a Not matcher.
589
    """A thing matched when it wasn't supposed to."""
590
591
    def __init__(self, matcher, other):
592
        self.matcher = matcher
593
        self.other = other
594
595
    def describe(self):
61.1.4 by Jonathan Lange
Revert the matcher change.
596
        return "%r matches %s" % (self.other, self.matcher)
61.1.1 by Jonathan Lange
Add an 'Annotation' file.
597
598
128.1.1 by Robert Collins
Add MatchesException matcher.
599
class MatchesException(Matcher):
128.1.5 by Robert Collins
Support matching exception types in MatchesException.
600
    """Match an exc_info tuple against an exception instance or type."""
128.1.1 by Robert Collins
Add MatchesException matcher.
601
169.1.4 by Jonathan Lange
Add option regular expression matching logic to MatchesException.
602
    def __init__(self, exception, value_re=None):
128.1.1 by Robert Collins
Add MatchesException matcher.
603
        """Create a MatchesException that will match exc_info's for exception.
128.1.10 by Jonathan Lange
Whitespace.
604
128.1.5 by Robert Collins
Support matching exception types in MatchesException.
605
        :param exception: Either an exception instance or type.
606
            If an instance is given, the type and arguments of the exception
607
            are checked. If a type is given only the type of the exception is
225.1.9 by Robert Collins
* ``MatchesException`` now permits a tuple of types rather than a single type
608
            checked. If a tuple is given, then as with isinstance, any of the
609
            types in the tuple matching is sufficient to match.
169.1.4 by Jonathan Lange
Add option regular expression matching logic to MatchesException.
610
        :param value_re: If 'exception' is a type, and the matchee exception
205.2.6 by Jonathan Lange
Docstring update.
611
            is of the right type, then match against this.  If value_re is a
612
            string, then assume value_re is a regular expression and match
613
            the str() of the exception against it.  Otherwise, assume value_re
614
            is a matcher, and match the exception against it.
128.1.1 by Robert Collins
Add MatchesException matcher.
615
        """
616
        Matcher.__init__(self)
617
        self.expected = exception
205.2.2 by Jonathan Lange
Allow value_re to be a matcher
618
        if istext(value_re):
216.1.2 by Jonathan Lange
ExpectedException will now match any exception of the type by default,
619
            value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
169.1.4 by Jonathan Lange
Add option regular expression matching logic to MatchesException.
620
        self.value_re = value_re
225.1.9 by Robert Collins
* ``MatchesException`` now permits a tuple of types rather than a single type
621
        self._is_instance = type(self.expected) not in classtypes() + (tuple,)
128.1.5 by Robert Collins
Support matching exception types in MatchesException.
622
128.1.1 by Robert Collins
Add MatchesException matcher.
623
    def match(self, other):
624
        if type(other) != tuple:
625
            return Mismatch('%r is not an exc_info tuple' % other)
130.1.4 by Martin
Rearrange MatchesException code and change str representation to work better with Python 2.4
626
        expected_class = self.expected
627
        if self._is_instance:
628
            expected_class = expected_class.__class__
629
        if not issubclass(other[0], expected_class):
630
            return Mismatch('%r is not a %r' % (other[0], expected_class))
169.1.4 by Jonathan Lange
Add option regular expression matching logic to MatchesException.
631
        if self._is_instance:
632
            if other[1].args != self.expected.args:
633
                return Mismatch('%s has different arguments to %s.' % (
634
                        _error_repr(other[1]), _error_repr(self.expected)))
635
        elif self.value_re is not None:
205.2.4 by Jonathan Lange
Oh, actually, make it work. Update the description tests.
636
            return self.value_re.match(other[1])
128.1.1 by Robert Collins
Add MatchesException matcher.
637
638
    def __str__(self):
130.1.4 by Martin
Rearrange MatchesException code and change str representation to work better with Python 2.4
639
        if self._is_instance:
640
            return "MatchesException(%s)" % _error_repr(self.expected)
130.1.6 by Martin
Go back to using repr for exception types in MatchesException.__str__
641
        return "MatchesException(%s)" % repr(self.expected)
128.1.1 by Robert Collins
Add MatchesException matcher.
642
643
225.1.1 by Robert Collins
New matcher Contains.
644
class Contains(Matcher):
645
    """Checks whether something is container in another thing."""
646
647
    def __init__(self, needle):
648
        """Create a Contains Matcher.
649
650
        :param needle: the thing that needs to be contained by matchees.
651
        """
652
        self.needle = needle
653
654
    def __str__(self):
655
        return "Contains(%r)" % (self.needle,)
656
657
    def match(self, matchee):
658
        try:
659
            if self.needle not in matchee:
660
                return DoesNotContain(matchee, self.needle)
661
        except TypeError:
662
            # e.g. 1 in 2 will raise TypeError
663
            return DoesNotContain(matchee, self.needle)
664
        return None
665
666
104 by Robert Collins
StartsWith and DoesNotStartWith matchers from Launchpad.
667
class StartsWith(Matcher):
668
    """Checks whether one string starts with another."""
669
670
    def __init__(self, expected):
671
        """Create a StartsWith Matcher.
672
673
        :param expected: the string that matchees should start with.
674
        """
675
        self.expected = expected
676
677
    def __str__(self):
219.4.19 by Martin
Make StartsWith and EndsWith stringify more like other matchers
678
        return "StartsWith(%r)" % (self.expected,)
104 by Robert Collins
StartsWith and DoesNotStartWith matchers from Launchpad.
679
680
    def match(self, matchee):
681
        if not matchee.startswith(self.expected):
682
            return DoesNotStartWith(matchee, self.expected)
683
        return None
684
685
138.1.1 by Jonathan Lange
EndsWith matcher.
686
class EndsWith(Matcher):
687
    """Checks whether one string starts with another."""
688
689
    def __init__(self, expected):
690
        """Create a EndsWith Matcher.
691
692
        :param expected: the string that matchees should end with.
693
        """
694
        self.expected = expected
695
696
    def __str__(self):
219.4.19 by Martin
Make StartsWith and EndsWith stringify more like other matchers
697
        return "EndsWith(%r)" % (self.expected,)
138.1.1 by Jonathan Lange
EndsWith matcher.
698
699
    def match(self, matchee):
700
        if not matchee.endswith(self.expected):
701
            return DoesNotEndWith(matchee, self.expected)
702
        return None
703
704
127.1.15 by Jonathan Lange
Implement KeysEqual matcher.
705
class KeysEqual(Matcher):
706
    """Checks whether a dict has particular keys."""
707
708
    def __init__(self, *expected):
709
        """Create a `KeysEqual` Matcher.
710
168.1.3 by Jonathan Lange
More fixes to epytext
711
        :param expected: The keys the dict is expected to have.  If a dict,
127.1.15 by Jonathan Lange
Implement KeysEqual matcher.
712
            then we use the keys of that dict, if a collection, we assume it
713
            is a collection of expected keys.
714
        """
715
        try:
716
            self.expected = expected.keys()
717
        except AttributeError:
718
            self.expected = list(expected)
719
720
    def __str__(self):
721
        return "KeysEqual(%s)" % ', '.join(map(repr, self.expected))
722
723
    def match(self, matchee):
724
        expected = sorted(self.expected)
725
        matched = Equals(expected).match(sorted(matchee.keys()))
726
        if matched:
727
            return AnnotatedMismatch(
728
                'Keys not equal',
729
                _BinaryMismatch(expected, 'does not match', matchee))
730
        return None
731
732
80.2.1 by Jamu Kakar
- All old-style classes are now new-style classes.
733
class Annotate(object):
61.1.1 by Jonathan Lange
Add an 'Annotation' file.
734
    """Annotates a matcher with a descriptive string.
735
736
    Mismatches are then described as '<mismatch>: <annotation>'.
737
    """
738
739
    def __init__(self, annotation, matcher):
740
        self.annotation = annotation
741
        self.matcher = matcher
742
193.1.3 by Jonathan Lange
Add a convenience method to Annotate that just passes through when there's
743
    @classmethod
744
    def if_message(cls, annotation, matcher):
745
        """Annotate ``matcher`` only if ``annotation`` is non-empty."""
746
        if not annotation:
747
            return matcher
748
        return cls(annotation, matcher)
749
61.1.4 by Jonathan Lange
Revert the matcher change.
750
    def __str__(self):
751
        return 'Annotate(%r, %s)' % (self.annotation, self.matcher)
61.1.1 by Jonathan Lange
Add an 'Annotation' file.
752
753
    def match(self, other):
754
        mismatch = self.matcher.match(other)
755
        if mismatch is not None:
756
            return AnnotatedMismatch(self.annotation, mismatch)
757
758
179.1.4 by Jonathan Lange
Use the decorator. Give the decorator a repr
759
class AnnotatedMismatch(MismatchDecorator):
61.1.1 by Jonathan Lange
Add an 'Annotation' file.
760
    """A mismatch annotated with a descriptive string."""
761
762
    def __init__(self, annotation, mismatch):
179.1.4 by Jonathan Lange
Use the decorator. Give the decorator a repr
763
        super(AnnotatedMismatch, self).__init__(mismatch)
61.1.1 by Jonathan Lange
Add an 'Annotation' file.
764
        self.annotation = annotation
765
        self.mismatch = mismatch
766
767
    def describe(self):
179.1.4 by Jonathan Lange
Use the decorator. Give the decorator a repr
768
        return '%s: %s' % (self.original.describe(), self.annotation)
179.1.1 by Jonathan Lange
Forward details from annotated mismatch.
769
128.1.2 by Robert Collins
Add a Raises matcher.
770
771
class Raises(Matcher):
128.1.6 by Robert Collins
Propograte KeyboardInterrupt from Raises.
772
    """Match if the matchee raises an exception when called.
128.1.10 by Jonathan Lange
Whitespace.
773
128.1.6 by Robert Collins
Propograte KeyboardInterrupt from Raises.
774
    Exceptions which are not subclasses of Exception propogate out of the
775
    Raises.match call unless they are explicitly matched.
776
    """
128.1.2 by Robert Collins
Add a Raises matcher.
777
778
    def __init__(self, exception_matcher=None):
128.1.10 by Jonathan Lange
Whitespace.
779
        """Create a Raises matcher.
780
128.1.2 by Robert Collins
Add a Raises matcher.
781
        :param exception_matcher: Optional validator for the exception raised
782
            by matchee. If supplied the exc_info tuple for the exception raised
783
            is passed into that matcher. If no exception_matcher is supplied
784
            then the simple fact of raising an exception is considered enough
785
            to match on.
786
        """
787
        self.exception_matcher = exception_matcher
788
789
    def match(self, matchee):
790
        try:
791
            result = matchee()
128.1.6 by Robert Collins
Propograte KeyboardInterrupt from Raises.
792
            return Mismatch('%r returned %r' % (matchee, result))
793
        # Catch all exceptions: Raises() should be able to match a
794
        # KeyboardInterrupt or SystemExit.
795
        except:
225.1.9 by Robert Collins
* ``MatchesException`` now permits a tuple of types rather than a single type
796
            exc_info = sys.exc_info()
128.1.2 by Robert Collins
Add a Raises matcher.
797
            if self.exception_matcher:
225.1.9 by Robert Collins
* ``MatchesException`` now permits a tuple of types rather than a single type
798
                mismatch = self.exception_matcher.match(exc_info)
128.1.6 by Robert Collins
Propograte KeyboardInterrupt from Raises.
799
                if not mismatch:
225.1.9 by Robert Collins
* ``MatchesException`` now permits a tuple of types rather than a single type
800
                    del exc_info
128.1.6 by Robert Collins
Propograte KeyboardInterrupt from Raises.
801
                    return
802
            else:
803
                mismatch = None
804
            # The exception did not match, or no explicit matching logic was
805
            # performed. If the exception is a non-user exception (that is, not
130.1.5 by Martin
Implement isbaseexception to resolve failures from exception hierarchy changes in Python 2.5
806
            # a subclass of Exception on Python 2.5+) then propogate it.
225.1.9 by Robert Collins
* ``MatchesException`` now permits a tuple of types rather than a single type
807
            if isbaseexception(exc_info[1]):
808
                del exc_info
130.1.1 by Martin
Use plain raise in Raises.match which is wrong on Python 2 but untested
809
                raise
128.1.6 by Robert Collins
Propograte KeyboardInterrupt from Raises.
810
            return mismatch
128.1.2 by Robert Collins
Add a Raises matcher.
811
812
    def __str__(self):
813
        return 'Raises()'
128.1.11 by Jonathan Lange
Add 'raises'
814
815
816
def raises(exception):
817
    """Make a matcher that checks that a callable raises an exception.
818
819
    This is a convenience function, exactly equivalent to::
168.1.2 by Jonathan Lange
Fix some epytext failures.
820
128.1.11 by Jonathan Lange
Add 'raises'
821
        return Raises(MatchesException(exception))
822
823
    See `Raises` and `MatchesException` for more information.
824
    """
825
    return Raises(MatchesException(exception))
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
826
827
155.1.10 by Jonathan Lange
Rename ``EachOf`` to ``MatchesListwise``
828
class MatchesListwise(object):
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
829
    """Matches if each matcher matches the corresponding value.
830
831
    More easily explained by example than in words:
832
155.1.10 by Jonathan Lange
Rename ``EachOf`` to ``MatchesListwise``
833
    >>> MatchesListwise([Equals(1)]).match([1])
834
    >>> MatchesListwise([Equals(1), Equals(2)]).match([1, 2])
186.2.1 by Martin
Refix various problems that broke Python 3 support
835
    >>> print (MatchesListwise([Equals(1), Equals(2)]).match([2, 1]).describe())
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
836
    Differences: [
837
    1 != 2
838
    2 != 1
839
    ]
233.2.7 by Jonathan Lange
Add a first_only option to MatchesListwise
840
    >>> matcher = MatchesListwise([Equals(1), Equals(2)], first_only=True)
841
    >>> print (matcher.match([3, 4]).describe())
842
    1 != 3
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
843
    """
844
233.2.7 by Jonathan Lange
Add a first_only option to MatchesListwise
845
    def __init__(self, matchers, first_only=False):
233.2.8 by Jonathan Lange
Documentation
846
        """Construct a MatchesListwise matcher.
847
848
        :param matchers: A list of matcher that the matched values must match.
849
        :param first_only: If True, then only report the first mismatch,
850
            otherwise report all of them. Defaults to False.
851
        """
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
852
        self.matchers = matchers
233.2.7 by Jonathan Lange
Add a first_only option to MatchesListwise
853
        self.first_only = first_only
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
854
855
    def match(self, values):
856
        mismatches = []
857
        length_mismatch = Annotate(
858
            "Length mismatch", Equals(len(self.matchers))).match(len(values))
859
        if length_mismatch:
860
            mismatches.append(length_mismatch)
861
        for matcher, value in zip(self.matchers, values):
862
            mismatch = matcher.match(value)
863
            if mismatch:
233.2.7 by Jonathan Lange
Add a first_only option to MatchesListwise
864
                if self.first_only:
865
                    return mismatch
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
866
                mismatches.append(mismatch)
867
        if mismatches:
868
            return MismatchesAll(mismatches)
869
870
871
class MatchesStructure(object):
872
    """Matcher that matches an object structurally.
873
874
    'Structurally' here means that attributes of the object being matched are
875
    compared against given matchers.
876
877
    `fromExample` allows the creation of a matcher from a prototype object and
878
    then modified versions can be created with `update`.
203.1.1 by Jonathan Lange
Add a 'byEquality' helper.
879
880
    `byEquality` creates a matcher in much the same way as the constructor,
881
    except that the matcher for each of the attributes is assumed to be
882
    `Equals`.
203.1.4 by Jonathan Lange
Docs and news update
883
884
    `byMatcher` creates a similar matcher to `byEquality`, but you get to pick
885
    the matcher, rather than just using `Equals`.
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
886
    """
887
888
    def __init__(self, **kwargs):
155.1.10 by Jonathan Lange
Rename ``EachOf`` to ``MatchesListwise``
889
        """Construct a `MatchesStructure`.
890
891
        :param kwargs: A mapping of attributes to matchers.
892
        """
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
893
        self.kws = kwargs
894
895
    @classmethod
203.1.1 by Jonathan Lange
Add a 'byEquality' helper.
896
    def byEquality(cls, **kwargs):
897
        """Matches an object where the attributes equal the keyword values.
898
899
        Similar to the constructor, except that the matcher is assumed to be
900
        Equals.
901
        """
203.1.3 by Jonathan Lange
Add byMatcher.
902
        return cls.byMatcher(Equals, **kwargs)
903
904
    @classmethod
905
    def byMatcher(cls, matcher, **kwargs):
203.1.4 by Jonathan Lange
Docs and news update
906
        """Matches an object where the attributes match the keyword values.
907
908
        Similar to the constructor, except that the provided matcher is used
909
        to match all of the values.
910
        """
203.1.3 by Jonathan Lange
Add byMatcher.
911
        return cls(
912
            **dict((name, matcher(value)) for name, value in kwargs.items()))
203.1.1 by Jonathan Lange
Add a 'byEquality' helper.
913
914
    @classmethod
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
915
    def fromExample(cls, example, *attributes):
916
        kwargs = {}
917
        for attr in attributes:
918
            kwargs[attr] = Equals(getattr(example, attr))
919
        return cls(**kwargs)
920
921
    def update(self, **kws):
922
        new_kws = self.kws.copy()
186.2.1 by Martin
Refix various problems that broke Python 3 support
923
        for attr, matcher in kws.items():
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
924
            if matcher is None:
925
                new_kws.pop(attr, None)
926
            else:
927
                new_kws[attr] = matcher
928
        return type(self)(**new_kws)
929
930
    def __str__(self):
931
        kws = []
186.2.1 by Martin
Refix various problems that broke Python 3 support
932
        for attr, matcher in sorted(self.kws.items()):
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
933
            kws.append("%s=%s" % (attr, matcher))
934
        return "%s(%s)" % (self.__class__.__name__, ', '.join(kws))
935
936
    def match(self, value):
937
        matchers = []
938
        values = []
186.2.1 by Martin
Refix various problems that broke Python 3 support
939
        for attr, matcher in sorted(self.kws.items()):
155.1.4 by Michael Hudson
add EachOf and MatchesStructure matchers
940
            matchers.append(Annotate(attr, matcher))
941
            values.append(getattr(value, attr))
155.1.10 by Jonathan Lange
Rename ``EachOf`` to ``MatchesListwise``
942
        return MatchesListwise(matchers).match(values)
155.1.5 by Michael Hudson
add MatchesRegex
943
944
945
class MatchesRegex(object):
946
    """Matches if the matchee is matched by a regular expression."""
947
948
    def __init__(self, pattern, flags=0):
949
        self.pattern = pattern
950
        self.flags = flags
951
952
    def __str__(self):
953
        args = ['%r' % self.pattern]
954
        flag_arg = []
955
        # dir() sorts the attributes for us, so we don't need to do it again.
956
        for flag in dir(re):
957
            if len(flag) == 1:
958
                if self.flags & getattr(re, flag):
959
                    flag_arg.append('re.%s' % flag)
960
        if flag_arg:
961
            args.append('|'.join(flag_arg))
962
        return '%s(%s)' % (self.__class__.__name__, ', '.join(args))
963
964
    def match(self, value):
965
        if not re.match(self.pattern, value, self.flags):
219.4.20 by Martin
Fix MatchesRegex mismatch description with a little transcoding dance
966
            pattern = self.pattern
967
            if not isinstance(pattern, str_is_unicode and str or unicode):
968
                pattern = pattern.decode("latin1")
969
            pattern = pattern.encode("unicode_escape").decode("ascii")
219.1.1 by Jonathan Lange
Nicer error message for regex fail.
970
            return Mismatch("%r does not match /%s/" % (
219.4.20 by Martin
Fix MatchesRegex mismatch description with a little transcoding dance
971
                    value, pattern.replace("\\\\", "\\")))
155.1.6 by Michael Hudson
add MatchesSetwise
972
973
974
class MatchesSetwise(object):
975
    """Matches if all the matchers match elements of the value being matched.
976
155.1.10 by Jonathan Lange
Rename ``EachOf`` to ``MatchesListwise``
977
    That is, each element in the 'observed' set must match exactly one matcher
978
    from the set of matchers, with no matchers left over.
979
980
    The difference compared to `MatchesListwise` is that the order of the
981
    matchings does not matter.
155.1.6 by Michael Hudson
add MatchesSetwise
982
    """
983
984
    def __init__(self, *matchers):
985
        self.matchers = matchers
986
987
    def match(self, observed):
988
        remaining_matchers = set(self.matchers)
989
        not_matched = []
990
        for value in observed:
991
            for matcher in remaining_matchers:
992
                if matcher.match(value) is None:
993
                    remaining_matchers.remove(matcher)
994
                    break
995
            else:
996
                not_matched.append(value)
997
        if not_matched or remaining_matchers:
998
            remaining_matchers = list(remaining_matchers)
999
            # There are various cases that all should be reported somewhat
1000
            # differently.
1001
1002
            # There are two trivial cases:
1003
            # 1) There are just some matchers left over.
1004
            # 2) There are just some values left over.
1005
1006
            # Then there are three more interesting cases:
1007
            # 3) There are the same number of matchers and values left over.
1008
            # 4) There are more matchers left over than values.
1009
            # 5) There are more values left over than matchers.
1010
1011
            if len(not_matched) == 0:
1012
                if len(remaining_matchers) > 1:
1013
                    msg = "There were %s matchers left over: " % (
1014
                        len(remaining_matchers),)
1015
                else:
1016
                    msg = "There was 1 matcher left over: "
1017
                msg += ', '.join(map(str, remaining_matchers))
1018
                return Mismatch(msg)
1019
            elif len(remaining_matchers) == 0:
1020
                if len(not_matched) > 1:
1021
                    return Mismatch(
1022
                        "There were %s values left over: %s" % (
1023
                            len(not_matched), not_matched))
1024
                else:
1025
                    return Mismatch(
1026
                        "There was 1 value left over: %s" % (
1027
                            not_matched, ))
1028
            else:
1029
                common_length = min(len(remaining_matchers), len(not_matched))
1030
                if common_length == 0:
1031
                    raise AssertionError("common_length can't be 0 here")
1032
                if common_length > 1:
1033
                    msg = "There were %s mismatches" % (common_length,)
1034
                else:
1035
                    msg = "There was 1 mismatch"
1036
                if len(remaining_matchers) > len(not_matched):
1037
                    extra_matchers = remaining_matchers[common_length:]
1038
                    msg += " and %s extra matcher" % (len(extra_matchers), )
1039
                    if len(extra_matchers) > 1:
1040
                        msg += "s"
1041
                    msg += ': ' + ', '.join(map(str, extra_matchers))
1042
                elif len(not_matched) > len(remaining_matchers):
1043
                    extra_values = not_matched[common_length:]
1044
                    msg += " and %s extra value" % (len(extra_values), )
1045
                    if len(extra_values) > 1:
1046
                        msg += "s"
1047
                    msg += ': ' + str(extra_values)
1048
                return Annotate(
155.1.10 by Jonathan Lange
Rename ``EachOf`` to ``MatchesListwise``
1049
                    msg, MatchesListwise(remaining_matchers[:common_length])
155.1.6 by Michael Hudson
add MatchesSetwise
1050
                    ).match(not_matched[:common_length])
155.1.7 by Michael Hudson
add AfterPreprocessing
1051
1052
204.1.1 by Jonathan Lange
Rename AfterPreproccessing to AfterPreprocessing, keeping a backward compatibility shim.
1053
class AfterPreprocessing(object):
155.1.7 by Michael Hudson
add AfterPreprocessing
1054
    """Matches if the value matches after passing through a function.
1055
1056
    This can be used to aid in creating trivial matchers as functions, for
175.1.1 by Jonathan Lange
Correct REST
1057
    example::
155.1.7 by Michael Hudson
add AfterPreprocessing
1058
175.1.1 by Jonathan Lange
Correct REST
1059
      def PathHasFileContent(content):
1060
          def _read(path):
1061
              return open(path).read()
204.1.3 by Jonathan Lange
Bring docs up to date with new spelling.
1062
          return AfterPreprocessing(_read, Equals(content))
155.1.7 by Michael Hudson
add AfterPreprocessing
1063
    """
1064
216.1.1 by Jonathan Lange
Add an option to AfterPreprocessing to hide the extra annotation.
1065
    def __init__(self, preprocessor, matcher, annotate=True):
1066
        """Create an AfterPreprocessing matcher.
1067
1068
        :param preprocessor: A function called with the matchee before
1069
            matching.
1070
        :param matcher: What to match the preprocessed matchee against.
1071
        :param annotate: Whether or not to annotate the matcher with
1072
            something explaining how we transformed the matchee. Defaults
1073
            to True.
1074
        """
155.1.7 by Michael Hudson
add AfterPreprocessing
1075
        self.preprocessor = preprocessor
1076
        self.matcher = matcher
216.1.1 by Jonathan Lange
Add an option to AfterPreprocessing to hide the extra annotation.
1077
        self.annotate = annotate
155.1.7 by Michael Hudson
add AfterPreprocessing
1078
1079
    def _str_preprocessor(self):
1080
        if isinstance(self.preprocessor, types.FunctionType):
1081
            return '<function %s>' % self.preprocessor.__name__
1082
        return str(self.preprocessor)
1083
1084
    def __str__(self):
204.1.1 by Jonathan Lange
Rename AfterPreproccessing to AfterPreprocessing, keeping a backward compatibility shim.
1085
        return "AfterPreprocessing(%s, %s)" % (
155.1.7 by Michael Hudson
add AfterPreprocessing
1086
            self._str_preprocessor(), self.matcher)
1087
1088
    def match(self, value):
205.2.5 by Jonathan Lange
Make the error message a little better.
1089
        after = self.preprocessor(value)
216.1.1 by Jonathan Lange
Add an option to AfterPreprocessing to hide the extra annotation.
1090
        if self.annotate:
1091
            matcher = Annotate(
1092
                "after %s on %r" % (self._str_preprocessor(), value),
1093
                self.matcher)
1094
        else:
1095
            matcher = self.matcher
1096
        return matcher.match(after)
203.2.1 by Jonathan Lange
Hide many testtools frames on the stack.
1097
204.1.2 by Jonathan Lange
NEWS update & comments.
1098
# This is the old, deprecated. spelling of the name, kept for backwards
1099
# compatibility.
204.1.1 by Jonathan Lange
Rename AfterPreproccessing to AfterPreprocessing, keeping a backward compatibility shim.
1100
AfterPreproccessing = AfterPreprocessing
203.2.8 by Jonathan Lange
Merge AfterPreprocessing fix
1101
203.2.1 by Jonathan Lange
Hide many testtools frames on the stack.
1102
204.2.1 by Jonathan Lange
Add a new matcher.
1103
class AllMatch(object):
1104
    """Matches if all provided values match the given matcher."""
1105
1106
    def __init__(self, matcher):
1107
        self.matcher = matcher
1108
1109
    def __str__(self):
1110
        return 'AllMatch(%s)' % (self.matcher,)
1111
1112
    def match(self, values):
1113
        mismatches = []
1114
        for value in values:
1115
            mismatch = self.matcher.match(value)
1116
            if mismatch:
1117
                mismatches.append(mismatch)
1118
        if mismatches:
1119
            return MismatchesAll(mismatches)
203.2.7 by Jonathan Lange
Merge all-match helper in.
1120
1121
233.2.6 by Jonathan Lange
Remove heaps of boiler plate by using MatchesPredicate
1122
def PathExists():
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1123
    """Matches if the given path exists.
1124
1125
    Use like this::
1126
1127
      assertThat('/some/path', PathExists())
1128
    """
233.2.6 by Jonathan Lange
Remove heaps of boiler plate by using MatchesPredicate
1129
    return MatchesPredicate(os.path.exists, "%s does not exist.")
1130
1131
1132
def DirExists():
1133
    """Matches if the path exists and is a directory."""
1134
    return MatchesAll(
1135
        PathExists(),
1136
        MatchesPredicate(os.path.isdir, "%s is not a directory."),
1137
        first_only=True)
1138
1139
1140
def FileExists():
233.2.3 by Jonathan Lange
Add FileExists and correct tests for DirExists
1141
    """Matches if the given path exists and is a file."""
233.2.6 by Jonathan Lange
Remove heaps of boiler plate by using MatchesPredicate
1142
    return MatchesAll(
1143
        PathExists(),
1144
        MatchesPredicate(os.path.isfile, "%s is not a file."),
1145
        first_only=True)
233.2.3 by Jonathan Lange
Add FileExists and correct tests for DirExists
1146
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1147
1148
class DirContains(Matcher):
1149
    """Matches if the given directory contains files with the given names.
1150
1151
    That is, is the directory listing exactly equal to the given files?
1152
    """
1153
233.2.18 by Jonathan Lange
DirContains now takes a matcher also
1154
    def __init__(self, filenames=None, matcher=None):
1155
        """Construct a ``DirContains`` matcher.
1156
233.2.19 by Jonathan Lange
Allow passing arbitrary matcher to FileContains.
1157
        Can be used in a basic mode where the whole directory listing is
1158
        matched against an expected directory listing (by passing
1159
        ``filenames``).  Can also be used in a more advanced way where the
1160
        whole directory listing is matched against an arbitrary matcher (by
1161
        passing ``matcher`` instead).
1162
233.2.18 by Jonathan Lange
DirContains now takes a matcher also
1163
        :param filenames: If specified, match the sorted directory listing
1164
            against this list of filenames, sorted.
1165
        :param matcher: If specified, match the sorted directory listing
1166
            against this matcher.
1167
        """
1168
        if filenames == matcher == None:
1169
            raise AssertionError(
1170
                "Must provide one of `filenames` or `matcher`.")
1171
        if None not in (filenames, matcher):
1172
            raise AssertionError(
1173
                "Must provide either `filenames` or `matcher`, not both.")
1174
        if filenames is None:
1175
            self.matcher = matcher
1176
        else:
1177
            self.matcher = Equals(sorted(filenames))
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1178
1179
    def match(self, path):
233.2.2 by Jonathan Lange
Even more tests
1180
        mismatch = DirExists().match(path)
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1181
        if mismatch is not None:
1182
            return mismatch
233.2.18 by Jonathan Lange
DirContains now takes a matcher also
1183
        return self.matcher.match(sorted(os.listdir(path)))
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1184
1185
1186
class FileContains(Matcher):
1187
    """Matches if the given file has the specified contents."""
1188
233.2.19 by Jonathan Lange
Allow passing arbitrary matcher to FileContains.
1189
    def __init__(self, contents=None, matcher=None):
1190
        """Construct a ``FileContains`` matcher.
1191
1192
        Can be used in a basic mode where the file contents are compared for
1193
        equality against the expected file contents (by passing ``contents``).
1194
        Can also be used in a more advanced way where the file contents are
1195
        matched against an arbitrary matcher (by passing ``matcher`` instead).
1196
1197
        :param contents: If specified, match the contents of the file with
1198
            these contents.
1199
        :param matcher: If specified, match the contents of the file against
1200
            this matcher.
1201
        """
1202
        if contents == matcher == None:
1203
            raise AssertionError(
1204
                "Must provide one of `contents` or `matcher`.")
1205
        if None not in (contents, matcher):
1206
            raise AssertionError(
1207
                "Must provide either `contents` or `matcher`, not both.")
1208
        if matcher is None:
1209
            self.matcher = Equals(contents)
1210
        else:
1211
            self.matcher = matcher
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1212
1213
    def match(self, path):
1214
        mismatch = PathExists().match(path)
1215
        if mismatch is not None:
1216
            return mismatch
233.2.11 by Jonathan Lange
More matchers still.
1217
        f = open(path)
1218
        try:
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1219
            actual_contents = f.read()
233.2.19 by Jonathan Lange
Allow passing arbitrary matcher to FileContains.
1220
            return self.matcher.match(actual_contents)
233.2.11 by Jonathan Lange
More matchers still.
1221
        finally:
1222
            f.close()
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1223
1224
    def __str__(self):
1225
        return "File at path exists and contains %s" % self.contents
1226
1227
233.2.9 by Jonathan Lange
Add tarball matcher.
1228
class TarballContains(Matcher):
1229
    """Matches if the given tarball contains the given paths.
1230
1231
    Uses TarFile.getnames() to get the paths out of the tarball.
1232
    """
1233
1234
    def __init__(self, paths):
1235
        super(TarballContains, self).__init__()
1236
        self.paths = paths
236.1.2 by Martin
Avoid fixed-in-py3k-only resource usage bug with reading .tar.gz files
1237
        self.path_matcher = Equals(sorted(self.paths))
233.2.9 by Jonathan Lange
Add tarball matcher.
1238
1239
    def match(self, tarball_path):
236.1.2 by Martin
Avoid fixed-in-py3k-only resource usage bug with reading .tar.gz files
1240
        # Open underlying file first to ensure it's always closed:
1241
        # <http://bugs.python.org/issue10233>
1242
        f = open(tarball_path, "rb")
233.2.9 by Jonathan Lange
Add tarball matcher.
1243
        try:
236.1.2 by Martin
Avoid fixed-in-py3k-only resource usage bug with reading .tar.gz files
1244
            tarball = tarfile.open(tarball_path, fileobj=f)
1245
            try:
1246
                return self.path_matcher.match(sorted(tarball.getnames()))
1247
            finally:
1248
                tarball.close()
233.2.9 by Jonathan Lange
Add tarball matcher.
1249
        finally:
236.1.2 by Martin
Avoid fixed-in-py3k-only resource usage bug with reading .tar.gz files
1250
            f.close()
233.2.9 by Jonathan Lange
Add tarball matcher.
1251
1252
233.2.10 by Jonathan Lange
Add SamePath
1253
class SamePath(Matcher):
233.2.11 by Jonathan Lange
More matchers still.
1254
    """Matches if two paths are the same.
1255
1256
    That is, the paths are equal, or they point to the same file but in
1257
    different ways.  The paths do not have to exist.
1258
    """
233.2.10 by Jonathan Lange
Add SamePath
1259
1260
    def __init__(self, path):
1261
        super(SamePath, self).__init__()
1262
        self.path = path
1263
1264
    def match(self, other_path):
1265
        f = lambda x: os.path.abspath(os.path.realpath(x))
1266
        return Equals(f(self.path)).match(f(other_path))
1267
1268
233.2.11 by Jonathan Lange
More matchers still.
1269
class HasPermissions(Matcher):
1270
    """Matches if a file has the given permissions.
1271
1272
    Permissions are specified and matched as a four-digit octal string.
1273
    """
1274
1275
    def __init__(self, octal_permissions):
1276
        """Construct a HasPermissions matcher.
1277
1278
        :param octal_permissions: A four digit octal string, representing the
1279
            intended access permissions. e.g. '0775' for rwxrwxr-x.
1280
        """
1281
        super(HasPermissions, self).__init__()
1282
        self.octal_permissions = octal_permissions
1283
1284
    def match(self, filename):
1285
        permissions = oct(os.stat(filename).st_mode)[-4:]
1286
        return Equals(self.octal_permissions).match(permissions)
233.2.1 by Jonathan Lange
Initial import of matchers from pkgme, plus some tests and some edits.
1287
1288
203.2.1 by Jonathan Lange
Hide many testtools frames on the stack.
1289
# Signal that this is part of the testing framework, and that code from this
1290
# should not normally appear in tracebacks.
1291
__unittest = True