|
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 |