pyhamcrest_metamatchers/metamatchers.py
from hamcrest.core.base_matcher import BaseMatcher
from hamcrest.core.string_description import StringDescription
class MetaMatcher(BaseMatcher):
def __init__(self, item, expected_match_result=True):
self.expected_match_result = expected_match_result
self.item = item
self.match_result = None
self.description = None
self.wrong_description = None
self.mismatch_description = None
self.wrong_mismatch_description = None
def with_description(self, description):
"""Adds the check for the description generated by the matcher that
is being tested. If this method is not called, the matcher will not
check the description at all.
If this method _is_ called, then the description, generated by the
matcher under test, will be checked. If the actual description doesn't
match the one set here, the metamatcher will not match.
"""
self.description = description
return self
def with_mismatch_description(self, mismatch_description):
"""Adds the check for the mismatch description generated by the
matcher being tested. The logic is the same as with
::py:meth:`pyhamcrest_metamatchers.metamatchers.MetaMatcher.with_description`
"""
self.mismatch_description = mismatch_description
return self
def _matches(self, matcher):
ret = True
self.match_result = matcher._matches(self.item)
if self.expected_match_result:
ret &= self.match_result
else:
ret &= not self.match_result
if self.description:
descr = StringDescription()
matcher.describe_to(descr)
descr = str(descr)
descr_is_correct = descr == self.description
if not descr_is_correct:
self.wrong_description = descr
ret = False
if self.mismatch_description:
if ret == self.expected_match_result:
self.wrong_mismatch_description = (
"the matcher matched, but a mismatch description was provided")
return False
descr = StringDescription()
matcher.describe_mismatch(self.item, descr)
descr = str(descr)
descr_is_correct = descr == self.mismatch_description
if not descr_is_correct:
self.wrong_mismatch_description = "the mismatch_description was <{}>".format(
descr)
ret = False
return ret
def describe_to(self, description):
ret = []
descriptions = []
ret.append("A matcher that {} the item".format(
"matches" if self.expected_match_result else "does not match"))
if self.description:
descriptions.append(
"with the description: <{}>".format(self.description))
if self.mismatch_description:
descriptions.append(
"with tne mismatch_description: <{}>".format(
self.mismatch_description))
ret.append(" and ".join(descriptions))
string = "{}.".format(" ".join(filter(None, ret)))
description.append_text(_sentence_case(string))
def describe_mismatch(self, item, mismatch_description):
ret = []
if self.match_result != self.expected_match_result:
ret.append("the matcher {}".format(
"matched" if self.match_result else "did not match"
))
if self.wrong_description:
ret.append("the description was <{}>".format(
self.wrong_description
))
if self.wrong_mismatch_description:
ret.append(
self.wrong_mismatch_description
)
string = "{}.".format(", ".join(ret))
mismatch_description.append_text(_sentence_case(string))
def matches(a_matcher):
"""Checks that the matcher under test matches the value
:param a_matcher: The matcher that needs to be checked.
:return: :py:class:`pyhamcrest_metamatchers.metamatchers.MetaMatcher<MetaMatcher>`
"""
return MetaMatcher(a_matcher)
def doesnt_match(a_matcher):
"""Checks that the matcher under test doesn't match the value
:param a_matcher: The matcher that needs to be checked.
:return: :py:class:`pyhamcrest_metamatchers.metamatchers.MetaMatcher<MetaMatcher>`
"""
return MetaMatcher(a_matcher, False)
def _sentence_case(string):
return "{}{}".format(string[0].capitalize(), string[1:])