mirror of
https://github.com/samba-team/samba.git
synced 2025-07-28 11:42:03 +03:00
170 lines
4.6 KiB
Python
170 lines
4.6 KiB
Python
# Copyright (c) 2009 Jonathan M. Lange. See LICENSE for details.
|
|
|
|
"""Matchers, a way to express complex assertions outside the testcase.
|
|
|
|
Inspired by 'hamcrest'.
|
|
|
|
Matcher provides the abstract API that all matchers need to implement.
|
|
|
|
Bundled matchers are listed in __all__: a list can be obtained by running
|
|
$ python -c 'import testtools.matchers; print testtools.matchers.__all__'
|
|
"""
|
|
|
|
__metaclass__ = type
|
|
__all__ = [
|
|
'DocTestMatches',
|
|
'Equals',
|
|
'MatchesAny',
|
|
]
|
|
|
|
import doctest
|
|
|
|
|
|
class Matcher:
|
|
"""A pattern matcher.
|
|
|
|
A Matcher must implement match and __str__ to be used by
|
|
testtools.TestCase.assertThat. Matcher.match(thing) returns None when
|
|
thing is completely matched, and a Mismatch object otherwise.
|
|
|
|
Matchers can be useful outside of test cases, as they are simply a
|
|
pattern matching language expressed as objects.
|
|
|
|
testtools.matchers is inspired by hamcrest, but is pythonic rather than
|
|
a Java transcription.
|
|
"""
|
|
|
|
def match(self, something):
|
|
"""Return None if this matcher matches something, a Mismatch otherwise.
|
|
"""
|
|
raise NotImplementedError(self.match)
|
|
|
|
def __str__(self):
|
|
"""Get a sensible human representation of the matcher.
|
|
|
|
This should include the parameters given to the matcher and any
|
|
state that would affect the matches operation.
|
|
"""
|
|
raise NotImplementedError(self.__str__)
|
|
|
|
|
|
class Mismatch:
|
|
"""An object describing a mismatch detected by a Matcher."""
|
|
|
|
def describe(self):
|
|
"""Describe the mismatch.
|
|
|
|
This should be either a human-readable string or castable to a string.
|
|
"""
|
|
raise NotImplementedError(self.describe_difference)
|
|
|
|
|
|
class DocTestMatches:
|
|
"""See if a string matches a doctest example."""
|
|
|
|
def __init__(self, example, flags=0):
|
|
"""Create a DocTestMatches to match example.
|
|
|
|
:param example: The example to match e.g. 'foo bar baz'
|
|
:param flags: doctest comparison flags to match on. e.g.
|
|
doctest.ELLIPSIS.
|
|
"""
|
|
if not example.endswith('\n'):
|
|
example += '\n'
|
|
self.want = example # required variable name by doctest.
|
|
self.flags = flags
|
|
self._checker = doctest.OutputChecker()
|
|
|
|
def __str__(self):
|
|
if self.flags:
|
|
flagstr = ", flags=%d" % self.flags
|
|
else:
|
|
flagstr = ""
|
|
return 'DocTestMatches(%r%s)' % (self.want, flagstr)
|
|
|
|
def _with_nl(self, actual):
|
|
result = str(actual)
|
|
if not result.endswith('\n'):
|
|
result += '\n'
|
|
return result
|
|
|
|
def match(self, actual):
|
|
with_nl = self._with_nl(actual)
|
|
if self._checker.check_output(self.want, with_nl, self.flags):
|
|
return None
|
|
return DocTestMismatch(self, with_nl)
|
|
|
|
def _describe_difference(self, with_nl):
|
|
return self._checker.output_difference(self, with_nl, self.flags)
|
|
|
|
|
|
class DocTestMismatch:
|
|
"""Mismatch object for DocTestMatches."""
|
|
|
|
def __init__(self, matcher, with_nl):
|
|
self.matcher = matcher
|
|
self.with_nl = with_nl
|
|
|
|
def describe(self):
|
|
return self.matcher._describe_difference(self.with_nl)
|
|
|
|
|
|
class Equals:
|
|
"""Matches if the items are equal."""
|
|
|
|
def __init__(self, expected):
|
|
self.expected = expected
|
|
|
|
def match(self, other):
|
|
if self.expected == other:
|
|
return None
|
|
return EqualsMismatch(self.expected, other)
|
|
|
|
def __str__(self):
|
|
return "Equals(%r)" % self.expected
|
|
|
|
|
|
class EqualsMismatch:
|
|
"""Two things differed."""
|
|
|
|
def __init__(self, expected, other):
|
|
self.expected = expected
|
|
self.other = other
|
|
|
|
def describe(self):
|
|
return "%r != %r" % (self.expected, self.other)
|
|
|
|
|
|
class MatchesAny:
|
|
"""Matches if any of the matchers it is created with match."""
|
|
|
|
def __init__(self, *matchers):
|
|
self.matchers = matchers
|
|
|
|
def match(self, matchee):
|
|
results = []
|
|
for matcher in self.matchers:
|
|
mismatch = matcher.match(matchee)
|
|
if mismatch is None:
|
|
return None
|
|
results.append(mismatch)
|
|
return MismatchesAll(results)
|
|
|
|
def __str__(self):
|
|
return "MatchesAny(%s)" % ', '.join([
|
|
str(matcher) for matcher in self.matchers])
|
|
|
|
|
|
class MismatchesAll:
|
|
"""A mismatch with many child mismatches."""
|
|
|
|
def __init__(self, mismatches):
|
|
self.mismatches = mismatches
|
|
|
|
def describe(self):
|
|
descriptions = ["Differences: ["]
|
|
for mismatch in self.mismatches:
|
|
descriptions.append(mismatch.describe())
|
|
descriptions.append("]\n")
|
|
return '\n'.join(descriptions)
|