1 # Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
21 class MatchesAny(object):
22 """Matches if any of the matchers it is created with match."""
24 def __init__(self, *matchers):
25 self.matchers = matchers
27 def match(self, matchee):
29 for matcher in self.matchers:
30 mismatch = matcher.match(matchee)
33 results.append(mismatch)
34 return MismatchesAll(results)
37 return "MatchesAny(%s)" % ', '.join([
38 str(matcher) for matcher in self.matchers])
41 class MatchesAll(object):
42 """Matches if all of the matchers it is created with match."""
44 def __init__(self, *matchers, **options):
45 """Construct a MatchesAll matcher.
47 Just list the component matchers as arguments in the ``*args``
48 style. If you want only the first mismatch to be reported, past in
49 first_only=True as a keyword argument. By default, all mismatches are
52 self.matchers = matchers
53 self.first_only = options.get('first_only', False)
56 return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers))
58 def match(self, matchee):
60 for matcher in self.matchers:
61 mismatch = matcher.match(matchee)
62 if mismatch is not None:
65 results.append(mismatch)
67 return MismatchesAll(results)
72 class MismatchesAll(Mismatch):
73 """A mismatch with many child mismatches."""
75 def __init__(self, mismatches, wrap=True):
76 self.mismatches = mismatches
82 descriptions = ["Differences: ["]
83 for mismatch in self.mismatches:
84 descriptions.append(mismatch.describe())
86 descriptions.append("]")
87 return '\n'.join(descriptions)
91 """Inverts a matcher."""
93 def __init__(self, matcher):
94 self.matcher = matcher
97 return 'Not(%s)' % (self.matcher,)
99 def match(self, other):
100 mismatch = self.matcher.match(other)
102 return MatchedUnexpectedly(self.matcher, other)
107 class MatchedUnexpectedly(Mismatch):
108 """A thing matched when it wasn't supposed to."""
110 def __init__(self, matcher, other):
111 self.matcher = matcher
115 return "%r matches %s" % (self.other, self.matcher)
118 class Annotate(object):
119 """Annotates a matcher with a descriptive string.
121 Mismatches are then described as '<mismatch>: <annotation>'.
124 def __init__(self, annotation, matcher):
125 self.annotation = annotation
126 self.matcher = matcher
129 def if_message(cls, annotation, matcher):
130 """Annotate ``matcher`` only if ``annotation`` is non-empty."""
133 return cls(annotation, matcher)
136 return 'Annotate(%r, %s)' % (self.annotation, self.matcher)
138 def match(self, other):
139 mismatch = self.matcher.match(other)
140 if mismatch is not None:
141 return AnnotatedMismatch(self.annotation, mismatch)
144 class PostfixedMismatch(MismatchDecorator):
145 """A mismatch annotated with a descriptive string."""
147 def __init__(self, annotation, mismatch):
148 super(PostfixedMismatch, self).__init__(mismatch)
149 self.annotation = annotation
150 self.mismatch = mismatch
153 return '%s: %s' % (self.original.describe(), self.annotation)
156 AnnotatedMismatch = PostfixedMismatch
159 class PrefixedMismatch(MismatchDecorator):
161 def __init__(self, prefix, mismatch):
162 super(PrefixedMismatch, self).__init__(mismatch)
166 return '%s: %s' % (self.prefix, self.original.describe())
169 class AfterPreprocessing(object):
170 """Matches if the value matches after passing through a function.
172 This can be used to aid in creating trivial matchers as functions, for
175 def PathHasFileContent(content):
177 return open(path).read()
178 return AfterPreprocessing(_read, Equals(content))
181 def __init__(self, preprocessor, matcher, annotate=True):
182 """Create an AfterPreprocessing matcher.
184 :param preprocessor: A function called with the matchee before
186 :param matcher: What to match the preprocessed matchee against.
187 :param annotate: Whether or not to annotate the matcher with
188 something explaining how we transformed the matchee. Defaults
191 self.preprocessor = preprocessor
192 self.matcher = matcher
193 self.annotate = annotate
195 def _str_preprocessor(self):
196 if isinstance(self.preprocessor, types.FunctionType):
197 return '<function %s>' % self.preprocessor.__name__
198 return str(self.preprocessor)
201 return "AfterPreprocessing(%s, %s)" % (
202 self._str_preprocessor(), self.matcher)
204 def match(self, value):
205 after = self.preprocessor(value)
208 "after %s on %r" % (self._str_preprocessor(), value),
211 matcher = self.matcher
212 return matcher.match(after)
215 # This is the old, deprecated. spelling of the name, kept for backwards
217 AfterPreproccessing = AfterPreprocessing
220 class AllMatch(object):
221 """Matches if all provided values match the given matcher."""
223 def __init__(self, matcher):
224 self.matcher = matcher
227 return 'AllMatch(%s)' % (self.matcher,)
229 def match(self, values):
232 mismatch = self.matcher.match(value)
234 mismatches.append(mismatch)
236 return MismatchesAll(mismatches)
239 class MatchesPredicate(Matcher):
240 """Match if a given function returns True.
242 It is reasonably common to want to make a very simple matcher based on a
243 function that you already have that returns True or False given a single
244 argument (i.e. a predicate function). This matcher makes it very easy to
247 IsEven = MatchesPredicate(lambda x: x % 2 == 0, '%s is not even')
248 self.assertThat(4, IsEven)
251 def __init__(self, predicate, message):
252 """Create a ``MatchesPredicate`` matcher.
254 :param predicate: A function that takes a single argument and returns
255 a value that will be interpreted as a boolean.
256 :param message: A message to describe a mismatch. It will be formatted
257 with '%' and be given whatever was passed to ``match()``. Thus, it
258 needs to contain exactly one thing like '%s', '%d' or '%f'.
260 self.predicate = predicate
261 self.message = message
264 return '%s(%r, %r)' % (
265 self.__class__.__name__, self.predicate, self.message)
268 if not self.predicate(x):
269 return Mismatch(self.message % x)