1
0
mirror of https://github.com/samba-team/samba.git synced 2025-07-28 11:42:03 +03:00
Files
samba-mirror/lib/subunit/python/testtools/matchers.py
2010-01-16 19:53:49 +13:00

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)