Source code for testsuite.protobuf.matching

import typing

import google.protobuf.message

from testsuite.matching import PartialDict, recursive_partial_dict
from testsuite.protobuf.utils import message_to_dict


class ProtobufDictsImpl:
    def __init__(self, repr: dict, impl: typing.Any):
        self._repr = repr
        self._impl = impl

    def __repr__(self):
        return f'<type(self).__name__ {self._repr!r}>'

    def __eq__(self, other):
        if isinstance(other, google.protobuf.message.Message):
            return message_to_dict(other) == self._impl
        if type(self) == type(other):
            return self._impl == other._impl
        return False


[docs] class ProtobufDict(ProtobufDictsImpl): """Strict protobuf matcher. Compares a protobuf message against an expected dict by converting the message via :py:func:`testsuite.protobuf.utils.message_to_dict` and requiring the resulting dict to equal the expected one exactly. Every field present in the message must appear in the expected dict (and vice versa). For partial matching where extra protobuf fields should be ignored, use :py:class:`PartialProtobufDict` or :py:func:`RecursivePartialProtobufDict` instead. Example: .. code-block:: python assert msg == matching.ProtobufDict({ 'first_name': 'John', 'last_name': 'Doe', 'status': 'STATUS_ACTIVE', }) """ __testsuite_types__ = (google.protobuf.message.Message,) def __init__(self, d: dict): super().__init__(repr=d, impl=d)
[docs] class PartialProtobufDict(ProtobufDictsImpl): """Partial protobuf matcher. Compares a protobuf message against an expected dict by converting the message via :py:func:`testsuite.protobuf.utils.message_to_dict` and delegating to :py:class:`testsuite.matching.PartialDict`. Only the keys listed in the expected dict are checked; any additional fields on the protobuf message are ignored. The match is only partial at the top level: nested dicts in the expected pattern still have to equal the corresponding nested message dicts exactly. Use :py:class:`RecursivePartialProtobufDict` when nested messages should also be matched partially. Example: .. code-block:: python # Passes regardless of other fields set on msg assert msg == matching.PartialProtobufDict({'first_name': 'John'}) """ def __init__(self, d): super().__init__(repr=d, impl=PartialDict(d))
[docs] class RecursivePartialProtobufDict(ProtobufDictsImpl): """Recursive partial protobuf matcher. Compares a protobuf message against an expected dict by converting the message via :py:func:`testsuite.protobuf.utils.message_to_dict` and delegating to :py:func:`testsuite.matching.recursive_partial_dict`. Unlike :py:class:`PartialProtobufDict`, which only ignores extra fields at the top level, this matcher applies partial matching recursively to every nested message: only the keys listed in the expected dict (at any depth) are checked, and any additional fields on the protobuf message or its nested submessages are ignored. Example: .. code-block:: python # Passes regardless of other fields set on msg or on # msg.nested_field, as long as the listed fields match. assert msg == matching.RecursivePartialProtobufDict({ 'first_name': 'John', 'nested_field': {'inner_field': 1}, }) """ def __init__(self, d): super().__init__(repr=d, impl=recursive_partial_dict(d))