ibolit/pyhamcrest_metamatchers

View on GitHub
pyhamcrest_metamatchers/metamatchers.py

Summary

Maintainability
A
1 hr
Test Coverage
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:])