# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Test the DictView class.
"""

from __future__ import absolute_import

import sys
import re
import pickle
from collections import OrderedDict
try:
    from collections.abc import KeysView, ValuesView, ItemsView, Iterator, \
        MutableMapping, Mapping
except ImportError:
    # pylint: disable=deprecated-class
    from collections import KeysView, ValuesView, ItemsView, Iterator, \
        MutableMapping, Mapping
import pytest
from nocasedict import NocaseDict, HashableMixin

from ..utils.simplified_test_function import simplified_test_function

# pylint: disable=wrong-import-position, wrong-import-order, invalid-name
from ..utils.import_installed import import_installed
immutable_views = import_installed('immutable_views')
from immutable_views import DictView  # noqa: E402
# pylint: enable=wrong-import-position, wrong-import-order, invalid-name


class HashableDict(HashableMixin, NocaseDict):
    """Hashable dictionary, for testing"""
    pass


class NorDict(Mapping):
    """Dictionary without OR operator, for testing"""

    def __init__(self, a_dict=None):
        if a_dict is None:
            a_dict = {}
        self._dict = dict(a_dict)

    def __getitem__(self, key):
        return self._dict[key]

    def __iter__(self):
        return iter(self._dict)

    def __len__(self):
        return len(self._dict)


class OrDict(NorDict):
    """Dictionary with OR operator, for testing"""

    def __or__(self, other):
        return self._dict | dict(other)

    def __ror__(self, other):
        return dict(other) | self._dict


class DerivedDict(dict):
    """Dictionary derived from dict with additional attribute"""

    def __init__(self, *args, **kwargs):
        super(DerivedDict, self).__init__(*args, **kwargs)
        self._dummy = 'dummy'

    def __eq__(self, other):
        super_equal = super(DerivedDict, self).__eq__(other)
        if isinstance(other, DerivedDict):
            return super_equal and self._dummy == other._dummy
        return super_equal


class DerivedDictView(DictView):
    """Dictionary view derived from DictView with additional attribute"""

    def __init__(self, *args, **kwargs):
        super(DerivedDictView, self).__init__(*args, **kwargs)
        self._dummy = 'dummy'

    def __eq__(self, other):
        super_equal = super(DerivedDictView, self).__eq__(other)
        if isinstance(other, DerivedDictView):
            return super_equal and self._dummy == other._dummy
        return super_equal


PY2 = sys.version_info[0] == 2

# Indicates Python dict supports lt/gt comparison (between dicts)
DICT_SUPPORTS_COMPARISON = sys.version_info[0:2] == (2, 7)

# Indicates Python dict is ordered
DICT_IS_ORDERED = sys.version_info[0:2] >= (3, 7)

# Indicates Python dict issues UserWarning about not preserving order
# of items in kwargs or in standard dict
DICT_WARNS_ORDER = sys.version_info[0:2] <= (3, 6)

# Indicates Python dict supports the iter..() and view..() methods
DICT_SUPPORTS_ITER_VIEW = sys.version_info[0:2] == (2, 7)

# Indicates Python dict supports the has_key() method
DICT_SUPPORTS_HAS_KEY = sys.version_info[0:2] == (2, 7)

# Indicates Python dict supports the __reversed__() method
DICT_SUPPORTS_REVERSED = sys.version_info[0:2] >= (3, 8)

# Indicates Python dict supports the __or__/__ror__() methods
DICT_SUPPORTS_OR = sys.version_info[0:2] >= (3, 9)

# Used as indicator not to pass an argument in the testcases.
# Note this has nothing to do with the _OMITTED flag in _immutable_views.py and
# could be a different value.
_OMIT_ARG = object()


class NonEquatable(object):
    # pylint: disable=too-few-public-methods
    """
    Class that raises TypeError when comparing its objects for equality.
    """

    def __eq__(self, other):
        raise TypeError("Cannot compare %s to %s" % (type(self), type(other)))

    def __ne__(self, other):
        raise TypeError("Cannot compare %s to %s" % (type(self), type(other)))


TESTCASES_DICTVIEW_INIT = [

    # Testcases for DictView.__init__() / ncd=DictView()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * init_args: Tuple of positional arguments to DictView().
    #   * init_kwargs: Dict of keyword arguments to DictView().
    #   * exp_dict: Expected resulting dictionary, as OrderedDict.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty underlying dictionaries
    (
        "From empty dict as positional arg",
        dict(
            init_args=({},),
            init_kwargs={},
            exp_dict=DictView({}),
            verify_order=True,
        ),
        None, None, True
    ),
    (
        "From empty OrderedDict as positional arg",
        dict(
            init_args=(OrderedDict(),),
            init_kwargs={},
            exp_dict=DictView(OrderedDict()),
            verify_order=True,
        ),
        None, None, True
    ),
    (
        "From DictView of empty dict as positional arg",
        dict(
            init_args=(DictView({}),),
            init_kwargs={},
            exp_dict=DictView({}),
            verify_order=True,
        ),
        None, None, True
    ),

    # Non-empty underlying dictionaries
    (
        "From non-empty dict as positional arg",
        dict(
            init_args=({'Dog': 'Cat', 'Budgie': 'Fish'},),
            init_kwargs={},
            exp_dict=DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),
            verify_order=False,
        ),
        None, None, True
    ),
    (
        "From DictView of non-empty dict as positional arg",
        dict(
            init_args=(DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),),
            init_kwargs={},
            exp_dict=DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),
            verify_order=False,
        ),
        None, None, True
    ),

    # Error cases with inputs that are invalid for the view but would be valid
    # for a real dictionary
    (
        "From no args (a_dict parameter required)",
        dict(
            init_args=(),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From None as positional arg (Must be Mapping type)",
        dict(
            init_args=(None,),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From empty list as positional arg (Must be Mapping type)",
        dict(
            init_args=(list(),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From empty tuple as positional arg (Must be Mapping type)",
        dict(
            init_args=(tuple(),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From list as positional arg (Must be Mapping type)",
        dict(
            init_args=([('Dog', 'Cat'), ('Budgie', 'Fish')],),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From tuple as positional arg (Must be Mapping type)",
        dict(
            init_args=((('Dog', 'Cat'), ('Budgie', 'Fish')),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From keyword args (Must be Mapping type)",
        dict(
            init_args=(),
            init_kwargs={'Dog': 'Cat', 'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From list as positional arg and keyword args (Must be Mapping type)",
        dict(
            init_args=([('Dog', 'Cat')],),
            init_kwargs={'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From tuple as positional arg and keyword args (Must be Mapping type)",
        dict(
            init_args=((('Dog', 'Cat'),),),
            init_kwargs={'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From dict as positional arg and keyword args (Must be Mapping type)",
        dict(
            init_args=({'Dog': 'Cat'},),
            init_kwargs={'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),

    # Other error cases
    (
        "String as positional arg (Must be Mapping type)",
        dict(
            init_args=('illegal',),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Integer as positional arg (Must be Mapping type)",
        dict(
            init_args=(42,),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Two positional args (too many args)",
        dict(
            init_args=(list(), list()),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "List as positional arg, whose item has only one item "
        "(Must be Mapping type)",
        dict(
            init_args=([('Dog',)],),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "List as positional arg, whose item has too many items "
        "(Must be Mapping type)",
        dict(
            init_args=([('Dog', 'Cat', 'bad')],),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Tuple as positional arg, whose item has only one item "
        "(Must be Mapping type)",
        dict(
            init_args=((('Dog',),),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Tuple as positional arg, whose item has too many items "
        "(Must be Mapping type)",
        dict(
            init_args=((('Dog', 'Cat', 'bad'),),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_INIT)
@simplified_test_function
def test_DictView_init(
        testcase, init_args, init_kwargs, exp_dict, verify_order):
    """
    Test function for DictView.__init__() / ncd=DictView()
    """

    # The code to be tested
    act_dict = DictView(*init_args, **init_kwargs)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # The verification below also uses some DictView features, but that is
    # unavoidable if we want to work through the public interface:

    act_items = []
    for key in act_dict:  # Uses DictView iteration
        act_value = act_dict[key]  # Uses DictView getitem
        assert key in exp_dict, "Unexpected extra key %r" % key
        exp_value = exp_dict[key]
        assert act_value == exp_value, "Unexpected value at key %r" % key
        act_items.append((key, act_value))

    exp_items = []
    for key in exp_dict:
        exp_value = exp_dict[key]
        # Next line uses DictView contains:
        assert key in act_dict, "Unexpected missing key %r" % key
        act_value = act_dict[key]  # Uses DictView getitem
        assert act_value == exp_value, "Unexpected value at key %r" % key
        exp_items.append((key, exp_value))

    if verify_order:
        assert act_items == exp_items, "Unexpected order of items"


TESTCASES_DICTVIEW_GETITEM = [

    # Testcases for DictView.__getitem__() / ncd[key]

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * exp_value: Expected value for the key.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty underlying dictionary
    (
        "Empty underlying dict, with None key (not found)",
        dict(
            obj=DictView({}),
            key=None,
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Empty underlying dict, with integer key (not found)",
        dict(
            obj=DictView({}),
            key=1234,
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Empty underlying dict, with empty string key (not found)",
        dict(
            obj=DictView({}),
            key='',
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Empty underlying dict, with non-empty key (not found)",
        dict(
            obj=DictView({}),
            key='Dog',
            exp_value=None,
        ),
        KeyError, None, True
    ),

    # Non-empty underlying dictionary
    (
        "Non-empty underlying dict, with None key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Non-empty underlying dict, with empty string key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Non-empty underlying dict, with non-existing key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Non-empty underlying dict, with existing key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            exp_value='Cat',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_GETITEM)
@simplified_test_function
def test_DictView_getitem(testcase, obj, key, exp_value):
    """
    Test function for DictView.__getitem__() / ncd[key]
    """

    # The code to be tested
    act_value = obj[key]

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_value == exp_value, "Unexpected value at key %r" % key


TESTCASES_DICTVIEW_LEN = [

    # Testcases for DictView.__len__() / len(ncd)

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * exp_len: Expected len() value.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
            exp_len=0,
        ),
        None, None, True
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            exp_len=2,
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_LEN)
@simplified_test_function
def test_DictView_len(testcase, obj, exp_len):
    """
    Test function for DictView.__len__() / len(ncd)
    """

    # The code to be tested
    act_len = len(obj)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_len == exp_len


TESTCASES_DICTVIEW_CONTAINS = [

    # Testcases for DictView.__contains__() / key in ncd

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * exp_result: Expected result (bool).
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty DictView
    (
        "Empty dict, with None key",
        dict(
            obj=DictView({}),
            key=None,
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Empty dict, with integer key",
        dict(
            obj=DictView({}),
            key=1234,
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Empty dict, with empty string key (not found)",
        dict(
            obj=DictView({}),
            key='',
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Empty dict, with non-empty key (not found)",
        dict(
            obj=DictView({}),
            key='Dog',
            exp_result=False,
        ),
        None, None, True
    ),

    # Non-empty DictView
    (
        "Non-empty dict, with non-existing None key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with empty string key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with non-empty key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with existing key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            exp_result=True,
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_CONTAINS)
@simplified_test_function
def test_DictView_contains(testcase, obj, key, exp_result):
    """
    Test function for DictView.__contains__() / key in ncd
    """

    # The code to be tested
    act_result = key in obj

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_result == exp_result, \
        "Unexpected result at key {k!r}".format(k=key)


TESTCASES_DICTVIEW_HAS_KEY = [

    # Testcases for DictView.has_key()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * exp_result: Expected result (bool).
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty DictView
    (
        "Empty dict, with None key",
        dict(
            obj=DictView({}),
            key=None,
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Empty dict, with integer key (no lower / success)",
        dict(
            obj=DictView({}),
            key=1234,
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Empty dict, with empty string key (not found)",
        dict(
            obj=DictView({}),
            key='',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Empty dict, with non-empty key (not found)",
        dict(
            obj=DictView({}),
            key='Dog',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),

    # Non-empty DictView
    (
        "Non-empty dict, with non-existing None key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Non-empty dict, with empty string key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Non-empty dict, with non-empty key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Non-empty dict, with existing key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            exp_result=True,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_HAS_KEY)
@simplified_test_function
def test_DictView_has_key(testcase, obj, key, exp_result):
    """
    Test function for DictView.has_key()
    """

    # The code to be tested
    act_result = obj.has_key(key)  # noqa: W601

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_result == exp_result, \
        "Unexpected result at key {k!r}".format(k=key)


TESTCASES_DICTVIEW_REVERSED = [

    # Testcases for DictView.__reversed__() / reversed(ncd)

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * exp_keys: Expected result as a list of keys, or None.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
            exp_keys=[],
        ),
        None if DICT_SUPPORTS_REVERSED else TypeError, None, DICT_IS_ORDERED
    ),
    (
        "Dict with one item",
        dict(
            obj=DictView({'Dog': 'Cat'}),
            exp_keys=['Dog'],
        ),
        None if DICT_SUPPORTS_REVERSED else TypeError, None, DICT_IS_ORDERED
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),
            exp_keys=['Budgie', 'Dog'],
        ),
        None if DICT_SUPPORTS_REVERSED else TypeError, None, DICT_IS_ORDERED
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_REVERSED)
@simplified_test_function
def test_DictView_reversed(testcase, obj, exp_keys):
    """
    Test function for DictView.__reversed__() / reversed(ncd)
    """

    # The code to be tested
    act_keys_iter = reversed(obj)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # The reason we verify that an iterator is returned is that
    # DictView.__reversed__() delegates to keys() which returns a list in
    # Python 2, so this verifies that reversed() still turns this into an
    # iterator.
    assert isinstance(act_keys_iter, Iterator)

    act_keys = list(act_keys_iter)
    assert act_keys == exp_keys


TESTCASES_DICTVIEW_GET = [

    # Testcases for DictView.get()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * default: Default value to be used for the test, or _OMIT_ARG.
    #   * exp_value: Expected value at the key.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty DictView
    (
        "Empty dict, with None key",
        dict(
            obj=DictView({}),
            key=None,
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with integer key",
        dict(
            obj=DictView({}),
            key=1234,
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with empty string key (defaulted without default)",
        dict(
            obj=DictView({}),
            key='',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with empty string key (defaulted to a value)",
        dict(
            obj=DictView({}),
            key='',
            default='Newbie',
            exp_value='Newbie',
        ),
        None, None, True
    ),
    (
        "Empty dict, with non-empty key (defaulted without default)",
        dict(
            obj=DictView({}),
            key='Dog',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with non-empty key (defaulted to a value)",
        dict(
            obj=DictView({}),
            key='Dog',
            default='Kitten',
            exp_value='Kitten',
        ),
        None, None, True
    ),

    # Non-empty DictView
    (
        "Non-empty dict, with None key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with empty string key (defaulted without default)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with empty string key (defaulted to a value)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            default='Newbie',
            exp_value='Newbie',
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with non-empty non-existing key (defaulted without "
        "default)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with non-empty non-existing key (defaulted to a "
        "value)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            default='Newbie',
            exp_value='Newbie',
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with existing key (no default)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            default=_OMIT_ARG,
            exp_value='Cat',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_GET)
@simplified_test_function
def test_DictView_get(testcase, obj, key, default, exp_value):
    """
    Test function for DictView.get()
    """

    # The code to be tested
    if default is _OMIT_ARG:
        act_value = obj.get(key)
    else:
        act_value = obj.get(key, default)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_value == exp_value, \
        "Unexpected value at key {k!r} with default {d}". \
        format(k=key, d="omitted" if default is _OMIT_ARG else repr(default))


TESTCASES_DICTVIEW_ITEMS = [

    # Testcases for DictView.keys(), values(), items()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * exp_items: List with expected items (key,value) in expected order.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
            exp_items=[],
        ),
        None, None, True
    ),
    (
        "Dict with one item",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat')])),
            exp_items=[('Dog', 'Cat')],
        ),
        None, None, True
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            exp_items=[('Dog', 'Cat'), ('Budgie', 'Fish')],
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_keys(testcase, obj, exp_items):
    """
    Test function for DictView.keys()
    """

    # The code to be tested
    act_keys = obj.keys()

    # Also test iterating through the result
    act_keys_list = list(act_keys)

    # Test that second iteration is possible
    act_keys_list2 = list(act_keys)

    if not PY2:

        # Test __contained__() of the returned view
        for key in act_keys_list:
            assert key in act_keys

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    if PY2:
        assert isinstance(act_keys, list)
    else:
        assert isinstance(act_keys, KeysView)

    exp_keys = [item[0] for item in exp_items]
    assert act_keys_list == exp_keys
    assert act_keys_list2 == exp_keys


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_values(testcase, obj, exp_items):
    """
    Test function for DictView.values()
    """

    # The code to be tested
    act_values = obj.values()

    # Also test iterating through the result
    act_values_list = list(act_values)

    # Test that second iteration is possible
    act_values_list2 = list(act_values)

    if not PY2:

        # Test __contained__() of the returned view
        for value in act_values_list:
            assert value in act_values

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    if PY2:
        assert isinstance(act_values, list)
    else:
        assert isinstance(act_values, ValuesView)

    exp_values = [item[1] for item in exp_items]
    assert act_values_list == exp_values
    assert act_values_list2 == exp_values


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_items(testcase, obj, exp_items):
    """
    Test function for DictView.items()
    """

    # The code to be tested
    act_items = obj.items()

    # Also test iterating through the result
    act_items_list = list(act_items)

    # Test that second iteration is possible
    act_items_list2 = list(act_items)

    if not PY2:

        # Test __contained__() of the returned view
        for item in act_items_list:
            assert item in act_items

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    if PY2:
        assert isinstance(act_items, list)
    else:
        assert isinstance(act_items, ItemsView)

    assert act_items_list == exp_items
    assert act_items_list2 == exp_items


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_iterkeys(testcase, obj, exp_items):
    """
    Test function for DictView.iterkeys()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support iterkeys() method")

    assert PY2

    # The code to be tested
    act_keys = []
    for key in obj.iterkeys():
        act_keys.append(key)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    exp_keys = [item[0] for item in exp_items]
    assert act_keys == exp_keys


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_itervalues(testcase, obj, exp_items):
    """
    Test function for DictView.itervalues()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support itervalues() method")

    assert PY2

    # The code to be tested
    act_values = []
    for value in obj.itervalues():
        act_values.append(value)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    exp_values = [item[1] for item in exp_items]
    assert act_values == exp_values


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_iteritems(testcase, obj, exp_items):
    """
    Test function for DictView.iteritemss()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support iteritems() method")

    assert PY2

    # The code to be tested
    act_items = []
    for item in obj.iteritems():
        act_items.append(item)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_items == exp_items


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_viewkeys(testcase, obj, exp_items):
    """
    Test function for DictView.viewkeys() (PY2 only)
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does support viewkeys() method")

    assert PY2

    # The code to be tested
    act_keys = obj.viewkeys()

    # Also test iterating through the result
    act_keys_list = list(act_keys)

    # Test that second iteration is possible
    act_keys_list2 = list(act_keys)

    # Test __contained__() of the returned view
    for key in act_keys_list:
        assert key in act_keys

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert isinstance(act_keys, KeysView)

    exp_keys = [item[0] for item in exp_items]
    assert act_keys_list == exp_keys
    assert act_keys_list2 == exp_keys


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_viewvalues(testcase, obj, exp_items):
    """
    Test function for DictView.viewvalues()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support viewvalues() method")

    assert PY2

    # The code to be tested
    act_values = obj.viewvalues()

    # Also test iterating through the result
    act_values_list = list(act_values)

    # Test that second iteration is possible
    act_values_list2 = list(act_values)

    # Test __contained__() of the returned view
    for value in act_values_list:
        assert value in act_values

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert isinstance(act_values, ValuesView)

    exp_values = [item[1] for item in exp_items]
    assert act_values_list == exp_values
    assert act_values_list2 == exp_values


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_viewitems(testcase, obj, exp_items):
    """
    Test function for DictView.viewitems()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support viewitems() method")

    assert PY2

    # The code to be tested
    act_items = obj.viewitems()

    # Also test iterating through the result
    act_items_list = list(act_items)

    # Test that second iteration is possible
    act_items_list2 = list(act_items)

    # Test __contained__() of the returned view
    for item in act_items_list:
        assert item in act_items

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert isinstance(act_items, ItemsView)

    assert act_items_list == exp_items
    assert act_items_list2 == exp_items


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_iter(testcase, obj, exp_items):
    """
    Test function for DictView.__iter__() / for key in ncd
    """

    # The code to be tested
    act_keys = []
    for key in obj:
        act_keys.append(key)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    exp_keys = [item[0] for item in exp_items]
    assert act_keys == exp_keys


TESTCASES_DICTVIEW_REPR = [

    # Testcases for DictView.__repr__() / repr(ncd)

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
        ),
        None, None, True
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_REPR)
@simplified_test_function
def test_DictView_repr(testcase, obj):
    """
    Test function for DictView.__repr__() / repr(ncd)
    """

    # The code to be tested
    result = repr(obj)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert re.match(r'^DictView\(.*\)$', result)

    # Note: This only tests for existence of each item, not for excess items
    # or representing the correct order.
    for item in obj.items():
        exp_item_result1 = "{0!r}: {1!r}".format(*item)
        exp_item_result2 = "({0!r}, {1!r})".format(*item)
        assert exp_item_result1 in result or exp_item_result2 in result


TESTCASES_DICTVIEW_COPY = [

    # Testcases for DictView.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * dictview: DictView object to be used for the test.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "DictView with empty dict",
        dict(
            dictview=DictView({}),
        ),
        None, None, True
    ),
    (
        "DictView with dict with two items",
        dict(
            dictview=DictView(
                dict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
        ),
        None, None, True
    ),
    (
        "DictView with OrderedDict with two items",
        dict(
            dictview=DictView(
                OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_COPY)
@simplified_test_function
def test_DictView_copy(testcase, dictview):
    """
    Test function for DictView.copy()
    """

    dictview_dict = dictview.dict

    # The code to be tested
    dictview_copy = dictview.copy()

    dictview_copy_dict = dictview_copy.dict

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # Verify the result type
    # pylint: disable=unidiomatic-typecheck
    assert type(dictview_copy) is DictView

    # Verify the result is a different object than the DictView
    assert id(dictview_copy) != id(dictview)

    # Verify the new dictionary is a different object than the underlying
    # dictionary, if mutable
    if isinstance(dictview_dict, MutableMapping):
        assert id(dictview_copy_dict) != id(dictview_dict)

    # Verify the new dictionary has the same type as the underlying dictionary
    # pylint: disable=unidiomatic-typecheck
    assert type(dictview_copy_dict) == type(dictview_dict)

    # Verify the new dictionary is equal to the underlying dictionary
    assert dictview_copy_dict == dictview_dict


TESTCASES_DICTVIEW_EQUAL = [

    # Testcases for DictView.__eq__(), __ne__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: DictView object #1 to use.
    #   * obj2: DictView object #2 to use.
    #   * exp_obj_equal: Expected equality of the objects.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dictionary",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "One item, keys and values equal",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([('k1', 'v1')])),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "One item, keys equal, values different",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([('k1', 'v1_x')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "One item, keys different, values equal",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([('k2', 'v1')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "One item, keys equal, values both None",
        dict(
            obj1=DictView(OrderedDict([('k1', None)])),
            obj2=DictView(OrderedDict([('k1', None)])),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Two equal items, in same order",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1'), ('k2', 'v2')])),
            obj2=DictView(OrderedDict([('k1', 'v1'), ('k2', 'v2')])),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Two equal items in ordered dict, in different order",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1'), ('k2', 'v2')])),
            obj2=DictView(OrderedDict([('k2', 'v2'), ('k1', 'v1')])),
            exp_obj_equal=False,
        ),
        None, None, False
    ),
    (
        "Two equal items in standard dict, in different order",
        dict(
            obj1=DictView(dict([('k1', 'v1'), ('k2', 'v2')])),
            obj2=DictView(dict([('k2', 'v2'), ('k1', 'v1')])),
            exp_obj_equal=not DICT_IS_ORDERED,
        ),
        None, None, False
    ),
    (
        "Comparing unicode value with bytes value",
        dict(
            obj1=DictView(OrderedDict([('k1', b'v1')])),
            obj2=DictView(OrderedDict([('k2', u'v2')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching unicode key with string key",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([(u'k2', 'v2')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Higher key missing",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Lower key missing",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Dog', 'Cat')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "First non-matching key is less. But longer size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([
                ('Budgie', 'Fish'), ('Curly', 'Snake'), ('Cozy', 'Dog'),
            ])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Only non-matching keys that are less. But longer size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict(
                [('Alf', 'F'), ('Anton', 'S'), ('Aussie', 'D')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "First non-matching key is greater. But shorter size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgio', 'Fish')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Only non-matching keys that are greater. But shorter size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Zoe', 'F')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size. First non-matching key is less",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict(
                [('Budgie', 'Fish'), ('Curly', 'Snake')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size. Only non-matching keys that are less",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Alf', 'F'), ('Anton', 'S')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size. Only non-matching keys that are greater",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Zoe', 'F'), ('Zulu', 'S')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size, only matching keys. First non-matching value is less",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Car')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size, only matching keys. First non-matching value is greater",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Caz')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "A value raises TypeError when compared (and equal still succeeds)",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict(
                [('Budgie', NonEquatable()), ('Dog', 'Cat')])),
            exp_obj_equal=False,
        ),
        TypeError, None, True
    ),

    (
        "Mixing types: DictView of dict and dict",
        dict(
            obj1=DictView(dict({'Dog': 'Cat'})),
            obj2=dict({'Dog': 'Cat'}),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: dict and DictView of dict",
        dict(
            obj1=dict({'Dog': 'Cat'}),
            obj2=DictView(dict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: DictView of dict and OrDict (not a dict)",
        dict(
            obj1=DictView(dict({'Dog': 'Cat'})),
            obj2=OrDict({'Dog': 'Cat'}),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: dict and DictView of OrDict (not a dict)",
        dict(
            obj1=dict({'Dog': 'Cat'}),
            obj2=DictView(OrDict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: DictView of OrDict (not a dict) and DictView of dict",
        dict(
            obj1=DictView(OrDict({'Dog': 'Cat'})),
            obj2=DictView(dict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: DictView of dict and DictView of OrDict (not a dict)",
        dict(
            obj1=DictView(dict({'Dog': 'Cat'})),
            obj2=DictView(OrDict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_EQUAL)
@simplified_test_function
def test_DictView_eq(testcase, obj1, obj2, exp_obj_equal):
    """
    Test function for DictView.__eq__() / ncd1==ncd2
    """

    # Double check they are different objects
    assert id(obj1) != id(obj2)

    # The code to be tested
    eq1 = (obj1 == obj2)
    eq2 = (obj2 == obj1)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert eq1 == exp_obj_equal
    assert eq2 == exp_obj_equal


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_EQUAL)
@simplified_test_function
def test_DictView_ne(testcase, obj1, obj2, exp_obj_equal):
    """
    Test function for DictView.__ne__() / ncd1!=ncd2
    """

    # Double check they are different objects
    assert id(obj1) != id(obj2)

    # The code to be tested
    ne1 = (obj1 != obj2)
    ne2 = (obj2 != obj1)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert ne1 != exp_obj_equal
    assert ne2 != exp_obj_equal


TESTCASES_DICTVIEW_ORDERING = [

    # Testcases for DictView.__le__(), __lt__(), __ge__(), __gt__() / ord.ops

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: DictView object #1 to be used.
    #   * obj2: DictView object #2 to be used.
    #   * op: Order comparison operator to be used, as a string (e.g. '>')
    #   * exp_result: Expected result of the comparison, or None.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty dicts
    (
        "Empty dicts with >",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='>',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Empty dicts with >=",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='>=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Empty dicts with <",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='<',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Empty dicts with <=",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='<=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Equal dicts
    (
        "Equal dicts with >",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Equal dicts with >=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Equal dicts with <",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Equal dicts with <=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Dicts that compare less (obj1 < obj2)
    (
        "Less-comparing dicts with >",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Less-comparing dicts with >=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>=',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Less-comparing dicts with <",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Less-comparing dicts with <=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Dicts that compare greater (obj1 > obj2)
    (
        "Greater-comparing dicts with >",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='>',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Greater-comparing dicts with >=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='>=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Greater-comparing dicts with <",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='<',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Greater-comparing dicts with <=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='<=',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Note: More subtle cases of less- or greater-comparing dicts are not
    # tested because the ordering comparison for DictView is deprecated.
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ORDERING)
@simplified_test_function
def test_DictView_ordering(testcase, obj1, obj2, op, exp_result):
    """
    Test function for DictView.__le__(), __lt__(), __ge__(), __gt__() / ord.
    """

    comp_str = 'obj1 %s obj2' % op

    # Double check they are different objects
    assert id(obj1) != id(obj2)

    # The code to be tested
    result = eval(comp_str)  # pylint: disable=eval-used

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert result == exp_result


TESTCASES_DICTVIEW_PICKLE = [

    # Testcases for pickling and unpickling DictView objects

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * dictview: DictView object to be used for the test.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "DictView of empty dict",
        dict(
            dictview=DictView({}),
        ),
        None, None, True
    ),
    (
        "DictView of dict with one item",
        dict(
            dictview=DictView({'Dog': 'Cat'}),
        ),
        None, None, True
    ),
    (
        "DictView of DerivedDict with one item",
        dict(
            dictview=DictView(DerivedDict({'Dog': 'Cat'})),
        ),
        None, None, True
    ),
    (
        "DerivedDictView of dict with one item",
        dict(
            dictview=DerivedDictView(dict({'Dog': 'Cat'})),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_PICKLE)
@simplified_test_function
def test_DictView_pickle(testcase, dictview):
    """
    Test function for pickling and unpickling DictView objects
    """

    # Pickle the object
    pkl = pickle.dumps(dictview)

    # Unpickle the object
    dictview2 = pickle.loads(pkl)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert dictview2 == dictview


TESTCASES_DICTVIEW_HASH = [

    # Testcases for DictView.__hash__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * dict_obj: Underlying dictionary object.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            dict_obj={},
        ),
        TypeError, None, True
    ),
    (
        "Empty OrderedDict",
        dict(
            dict_obj=OrderedDict(),
        ),
        TypeError, None, True
    ),
    (
        "Empty HashableDict",
        dict(
            dict_obj=HashableDict(),
        ),
        None, None, True
    ),

    (
        "Dict with one item",
        dict(
            dict_obj=dict([('a', 1)]),
        ),
        TypeError, None, True
    ),
    (
        "OrderedDict with one item",
        dict(
            dict_obj=OrderedDict([('a', 1)]),
        ),
        TypeError, None, True
    ),
    (
        "HashableDict with one item",
        dict(
            dict_obj=HashableDict([('a', 1)]),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_HASH)
@simplified_test_function
def test_DictView_hash(testcase, dict_obj):
    """
    Test function for DictView.__hash__() / hash()
    """

    view = DictView(dict_obj)

    # The code to be tested
    view_hash = hash(view)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # If it worked for the view, it must work for the underlying collection
    dict_hash = hash(dict_obj)

    # Verify the hash value of the underlying collection is used for the view
    assert view_hash == dict_hash


TESTCASES_DICTVIEW_OR = [

    # Testcases for DictView.__or__/__ror__() / self | or

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: DictView object #1 to be used.
    #   * obj2: DictView object #2 to be used.
    #   * exp_result: Expected result of the operation, or None.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "DictView of empty NorDict and DictView of empty NorDict",
        dict(
            obj1=DictView(NorDict()),
            obj2=DictView(NorDict()),
            exp_result=None,
        ),
        TypeError, None, True
    ),
    (
        "DictView of empty OrDict and DictView of empty NorDict",
        dict(
            obj1=DictView(OrDict()),
            obj2=DictView(NorDict()),
            exp_result=DictView(OrDict()),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "DictView of empty NorDict and DictView of empty OrDict",
        dict(
            obj1=DictView(NorDict()),
            obj2=DictView(OrDict()),
            exp_result=DictView(NorDict()),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "Empty NorDict and DictView of empty OrDict",
        dict(
            obj1=NorDict(),
            obj2=DictView(OrDict()),
            exp_result=NorDict(),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with two items and NorDict with equal two items",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with two items and OrDict with equal two items",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "Empty OrDict and NorDict with two items",
        dict(
            obj1=DictView(OrDict()),
            obj2=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "Empty NorDict and OrDict with two items",
        dict(
            obj1=DictView(NorDict()),
            obj2=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with two items and empty NorDict",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(NorDict()),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with two items and empty OrDict",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(OrDict()),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with one item and NorDict with one item with different key",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish'})),
            obj2=DictView(NorDict({'Dog': 'Cat'})),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with one item and OrDict with one item with different key",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish'})),
            obj2=DictView(OrDict({'Dog': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with one item and NorDict with one item with same key, "
        "different value",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish'})),
            obj2=DictView(NorDict({'Budgie': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with one item and OrDict with one item with same key, "
        "different value",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish'})),
            obj2=DictView(OrDict({'Budgie': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_OR)
@simplified_test_function
def test_DictView_or(testcase, obj1, obj2, exp_result):
    """
    Test function for DictView.__or__/__ror__() / self | or
    """

    # The code to be tested
    result = obj1 | obj2

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert result == exp_result

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Test the DictView class.
"""

from __future__ import absolute_import

import sys
import re
import pickle
from collections import OrderedDict
try:
    from collections.abc import KeysView, ValuesView, ItemsView, Iterator, \
        MutableMapping, Mapping
except ImportError:
    # pylint: disable=deprecated-class
    from collections import KeysView, ValuesView, ItemsView, Iterator, \
        MutableMapping, Mapping
import pytest
from nocasedict import NocaseDict, HashableMixin

from ..utils.simplified_test_function import simplified_test_function

# pylint: disable=wrong-import-position, wrong-import-order, invalid-name
from ..utils.import_installed import import_installed
immutable_views = import_installed('immutable_views')
from immutable_views import DictView  # noqa: E402
# pylint: enable=wrong-import-position, wrong-import-order, invalid-name


class HashableDict(HashableMixin, NocaseDict):
    """Hashable dictionary, for testing"""
    pass


class NorDict(Mapping):
    """Dictionary without OR operator, for testing"""

    def __init__(self, a_dict=None):
        if a_dict is None:
            a_dict = {}
        self._dict = dict(a_dict)

    def __getitem__(self, key):
        return self._dict[key]

    def __iter__(self):
        return iter(self._dict)

    def __len__(self):
        return len(self._dict)


class OrDict(NorDict):
    """Dictionary with OR operator, for testing"""

    def __or__(self, other):
        return self._dict | dict(other)

    def __ror__(self, other):
        return dict(other) | self._dict


class DerivedDict(dict):
    """Dictionary derived from dict with additional attribute"""

    def __init__(self, *args, **kwargs):
        super(DerivedDict, self).__init__(*args, **kwargs)
        self._dummy = 'dummy'

    def __eq__(self, other):
        super_equal = super(DerivedDict, self).__eq__(other)
        if isinstance(other, DerivedDict):
            return super_equal and self._dummy == other._dummy
        return super_equal


class DerivedDictView(DictView):
    """Dictionary view derived from DictView with additional attribute"""

    def __init__(self, *args, **kwargs):
        super(DerivedDictView, self).__init__(*args, **kwargs)
        self._dummy = 'dummy'

    def __eq__(self, other):
        super_equal = super(DerivedDictView, self).__eq__(other)
        if isinstance(other, DerivedDictView):
            return super_equal and self._dummy == other._dummy
        return super_equal


PY2 = sys.version_info[0] == 2

# Indicates Python dict supports lt/gt comparison (between dicts)
DICT_SUPPORTS_COMPARISON = sys.version_info[0:2] == (2, 7)

# Indicates Python dict is ordered
DICT_IS_ORDERED = sys.version_info[0:2] >= (3, 7)

# Indicates Python dict issues UserWarning about not preserving order
# of items in kwargs or in standard dict
DICT_WARNS_ORDER = sys.version_info[0:2] <= (3, 6)

# Indicates Python dict supports the iter..() and view..() methods
DICT_SUPPORTS_ITER_VIEW = sys.version_info[0:2] == (2, 7)

# Indicates Python dict supports the has_key() method
DICT_SUPPORTS_HAS_KEY = sys.version_info[0:2] == (2, 7)

# Indicates Python dict supports the __reversed__() method
DICT_SUPPORTS_REVERSED = sys.version_info[0:2] >= (3, 8)

# Indicates Python dict supports the __or__/__ror__() methods
DICT_SUPPORTS_OR = sys.version_info[0:2] >= (3, 9)

# Used as indicator not to pass an argument in the testcases.
# Note this has nothing to do with the _OMITTED flag in _immutable_views.py and
# could be a different value.
_OMIT_ARG = object()


class NonEquatable(object):
    # pylint: disable=too-few-public-methods
    """
    Class that raises TypeError when comparing its objects for equality.
    """

    def __eq__(self, other):
        raise TypeError("Cannot compare %s to %s" % (type(self), type(other)))

    def __ne__(self, other):
        raise TypeError("Cannot compare %s to %s" % (type(self), type(other)))


TESTCASES_DICTVIEW_INIT = [

    # Testcases for DictView.__init__() / ncd=DictView()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * init_args: Tuple of positional arguments to DictView().
    #   * init_kwargs: Dict of keyword arguments to DictView().
    #   * exp_dict: Expected resulting dictionary, as OrderedDict.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty underlying dictionaries
    (
        "From empty dict as positional arg",
        dict(
            init_args=({},),
            init_kwargs={},
            exp_dict=DictView({}),
            verify_order=True,
        ),
        None, None, True
    ),
    (
        "From empty OrderedDict as positional arg",
        dict(
            init_args=(OrderedDict(),),
            init_kwargs={},
            exp_dict=DictView(OrderedDict()),
            verify_order=True,
        ),
        None, None, True
    ),
    (
        "From DictView of empty dict as positional arg",
        dict(
            init_args=(DictView({}),),
            init_kwargs={},
            exp_dict=DictView({}),
            verify_order=True,
        ),
        None, None, True
    ),

    # Non-empty underlying dictionaries
    (
        "From non-empty dict as positional arg",
        dict(
            init_args=({'Dog': 'Cat', 'Budgie': 'Fish'},),
            init_kwargs={},
            exp_dict=DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),
            verify_order=False,
        ),
        None, None, True
    ),
    (
        "From DictView of non-empty dict as positional arg",
        dict(
            init_args=(DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),),
            init_kwargs={},
            exp_dict=DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),
            verify_order=False,
        ),
        None, None, True
    ),

    # Error cases with inputs that are invalid for the view but would be valid
    # for a real dictionary
    (
        "From no args (a_dict parameter required)",
        dict(
            init_args=(),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From None as positional arg (Must be Mapping type)",
        dict(
            init_args=(None,),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From empty list as positional arg (Must be Mapping type)",
        dict(
            init_args=(list(),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From empty tuple as positional arg (Must be Mapping type)",
        dict(
            init_args=(tuple(),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From list as positional arg (Must be Mapping type)",
        dict(
            init_args=([('Dog', 'Cat'), ('Budgie', 'Fish')],),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From tuple as positional arg (Must be Mapping type)",
        dict(
            init_args=((('Dog', 'Cat'), ('Budgie', 'Fish')),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From keyword args (Must be Mapping type)",
        dict(
            init_args=(),
            init_kwargs={'Dog': 'Cat', 'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From list as positional arg and keyword args (Must be Mapping type)",
        dict(
            init_args=([('Dog', 'Cat')],),
            init_kwargs={'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From tuple as positional arg and keyword args (Must be Mapping type)",
        dict(
            init_args=((('Dog', 'Cat'),),),
            init_kwargs={'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "From dict as positional arg and keyword args (Must be Mapping type)",
        dict(
            init_args=({'Dog': 'Cat'},),
            init_kwargs={'Budgie': 'Fish'},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),

    # Other error cases
    (
        "String as positional arg (Must be Mapping type)",
        dict(
            init_args=('illegal',),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Integer as positional arg (Must be Mapping type)",
        dict(
            init_args=(42,),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Two positional args (too many args)",
        dict(
            init_args=(list(), list()),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "List as positional arg, whose item has only one item "
        "(Must be Mapping type)",
        dict(
            init_args=([('Dog',)],),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "List as positional arg, whose item has too many items "
        "(Must be Mapping type)",
        dict(
            init_args=([('Dog', 'Cat', 'bad')],),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Tuple as positional arg, whose item has only one item "
        "(Must be Mapping type)",
        dict(
            init_args=((('Dog',),),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
    (
        "Tuple as positional arg, whose item has too many items "
        "(Must be Mapping type)",
        dict(
            init_args=((('Dog', 'Cat', 'bad'),),),
            init_kwargs={},
            exp_dict=None,
            verify_order=None,
        ),
        TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_INIT)
@simplified_test_function
def test_DictView_init(
        testcase, init_args, init_kwargs, exp_dict, verify_order):
    """
    Test function for DictView.__init__() / ncd=DictView()
    """

    # The code to be tested
    act_dict = DictView(*init_args, **init_kwargs)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # The verification below also uses some DictView features, but that is
    # unavoidable if we want to work through the public interface:

    act_items = []
    for key in act_dict:  # Uses DictView iteration
        act_value = act_dict[key]  # Uses DictView getitem
        assert key in exp_dict, "Unexpected extra key %r" % key
        exp_value = exp_dict[key]
        assert act_value == exp_value, "Unexpected value at key %r" % key
        act_items.append((key, act_value))

    exp_items = []
    for key in exp_dict:
        exp_value = exp_dict[key]
        # Next line uses DictView contains:
        assert key in act_dict, "Unexpected missing key %r" % key
        act_value = act_dict[key]  # Uses DictView getitem
        assert act_value == exp_value, "Unexpected value at key %r" % key
        exp_items.append((key, exp_value))

    if verify_order:
        assert act_items == exp_items, "Unexpected order of items"


TESTCASES_DICTVIEW_GETITEM = [

    # Testcases for DictView.__getitem__() / ncd[key]

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * exp_value: Expected value for the key.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty underlying dictionary
    (
        "Empty underlying dict, with None key (not found)",
        dict(
            obj=DictView({}),
            key=None,
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Empty underlying dict, with integer key (not found)",
        dict(
            obj=DictView({}),
            key=1234,
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Empty underlying dict, with empty string key (not found)",
        dict(
            obj=DictView({}),
            key='',
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Empty underlying dict, with non-empty key (not found)",
        dict(
            obj=DictView({}),
            key='Dog',
            exp_value=None,
        ),
        KeyError, None, True
    ),

    # Non-empty underlying dictionary
    (
        "Non-empty underlying dict, with None key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Non-empty underlying dict, with empty string key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Non-empty underlying dict, with non-existing key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            exp_value=None,
        ),
        KeyError, None, True
    ),
    (
        "Non-empty underlying dict, with existing key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            exp_value='Cat',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_GETITEM)
@simplified_test_function
def test_DictView_getitem(testcase, obj, key, exp_value):
    """
    Test function for DictView.__getitem__() / ncd[key]
    """

    # The code to be tested
    act_value = obj[key]

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_value == exp_value, "Unexpected value at key %r" % key


TESTCASES_DICTVIEW_LEN = [

    # Testcases for DictView.__len__() / len(ncd)

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * exp_len: Expected len() value.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
            exp_len=0,
        ),
        None, None, True
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            exp_len=2,
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_LEN)
@simplified_test_function
def test_DictView_len(testcase, obj, exp_len):
    """
    Test function for DictView.__len__() / len(ncd)
    """

    # The code to be tested
    act_len = len(obj)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_len == exp_len


TESTCASES_DICTVIEW_CONTAINS = [

    # Testcases for DictView.__contains__() / key in ncd

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * exp_result: Expected result (bool).
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty DictView
    (
        "Empty dict, with None key",
        dict(
            obj=DictView({}),
            key=None,
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Empty dict, with integer key",
        dict(
            obj=DictView({}),
            key=1234,
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Empty dict, with empty string key (not found)",
        dict(
            obj=DictView({}),
            key='',
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Empty dict, with non-empty key (not found)",
        dict(
            obj=DictView({}),
            key='Dog',
            exp_result=False,
        ),
        None, None, True
    ),

    # Non-empty DictView
    (
        "Non-empty dict, with non-existing None key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with empty string key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with non-empty key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            exp_result=False,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with existing key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            exp_result=True,
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_CONTAINS)
@simplified_test_function
def test_DictView_contains(testcase, obj, key, exp_result):
    """
    Test function for DictView.__contains__() / key in ncd
    """

    # The code to be tested
    act_result = key in obj

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_result == exp_result, \
        "Unexpected result at key {k!r}".format(k=key)


TESTCASES_DICTVIEW_HAS_KEY = [

    # Testcases for DictView.has_key()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * exp_result: Expected result (bool).
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty DictView
    (
        "Empty dict, with None key",
        dict(
            obj=DictView({}),
            key=None,
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Empty dict, with integer key (no lower / success)",
        dict(
            obj=DictView({}),
            key=1234,
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Empty dict, with empty string key (not found)",
        dict(
            obj=DictView({}),
            key='',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Empty dict, with non-empty key (not found)",
        dict(
            obj=DictView({}),
            key='Dog',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),

    # Non-empty DictView
    (
        "Non-empty dict, with non-existing None key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Non-empty dict, with empty string key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Non-empty dict, with non-empty key (not found)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            exp_result=False,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
    (
        "Non-empty dict, with existing key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            exp_result=True,
        ),
        AttributeError if not DICT_SUPPORTS_HAS_KEY else None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_HAS_KEY)
@simplified_test_function
def test_DictView_has_key(testcase, obj, key, exp_result):
    """
    Test function for DictView.has_key()
    """

    # The code to be tested
    act_result = obj.has_key(key)  # noqa: W601

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_result == exp_result, \
        "Unexpected result at key {k!r}".format(k=key)


TESTCASES_DICTVIEW_REVERSED = [

    # Testcases for DictView.__reversed__() / reversed(ncd)

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * exp_keys: Expected result as a list of keys, or None.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
            exp_keys=[],
        ),
        None if DICT_SUPPORTS_REVERSED else TypeError, None, DICT_IS_ORDERED
    ),
    (
        "Dict with one item",
        dict(
            obj=DictView({'Dog': 'Cat'}),
            exp_keys=['Dog'],
        ),
        None if DICT_SUPPORTS_REVERSED else TypeError, None, DICT_IS_ORDERED
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView({'Dog': 'Cat', 'Budgie': 'Fish'}),
            exp_keys=['Budgie', 'Dog'],
        ),
        None if DICT_SUPPORTS_REVERSED else TypeError, None, DICT_IS_ORDERED
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_REVERSED)
@simplified_test_function
def test_DictView_reversed(testcase, obj, exp_keys):
    """
    Test function for DictView.__reversed__() / reversed(ncd)
    """

    # The code to be tested
    act_keys_iter = reversed(obj)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # The reason we verify that an iterator is returned is that
    # DictView.__reversed__() delegates to keys() which returns a list in
    # Python 2, so this verifies that reversed() still turns this into an
    # iterator.
    assert isinstance(act_keys_iter, Iterator)

    act_keys = list(act_keys_iter)
    assert act_keys == exp_keys


TESTCASES_DICTVIEW_GET = [

    # Testcases for DictView.get()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * key: Key to be used for the test.
    #   * default: Default value to be used for the test, or _OMIT_ARG.
    #   * exp_value: Expected value at the key.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty DictView
    (
        "Empty dict, with None key",
        dict(
            obj=DictView({}),
            key=None,
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with integer key",
        dict(
            obj=DictView({}),
            key=1234,
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with empty string key (defaulted without default)",
        dict(
            obj=DictView({}),
            key='',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with empty string key (defaulted to a value)",
        dict(
            obj=DictView({}),
            key='',
            default='Newbie',
            exp_value='Newbie',
        ),
        None, None, True
    ),
    (
        "Empty dict, with non-empty key (defaulted without default)",
        dict(
            obj=DictView({}),
            key='Dog',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Empty dict, with non-empty key (defaulted to a value)",
        dict(
            obj=DictView({}),
            key='Dog',
            default='Kitten',
            exp_value='Kitten',
        ),
        None, None, True
    ),

    # Non-empty DictView
    (
        "Non-empty dict, with None key",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key=None,
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with empty string key (defaulted without default)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with empty string key (defaulted to a value)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='',
            default='Newbie',
            exp_value='Newbie',
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with non-empty non-existing key (defaulted without "
        "default)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            default=_OMIT_ARG,
            exp_value=None,
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with non-empty non-existing key (defaulted to a "
        "value)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='invalid',
            default='Newbie',
            exp_value='Newbie',
        ),
        None, None, True
    ),
    (
        "Non-empty dict, with existing key (no default)",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            key='Dog',
            default=_OMIT_ARG,
            exp_value='Cat',
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_GET)
@simplified_test_function
def test_DictView_get(testcase, obj, key, default, exp_value):
    """
    Test function for DictView.get()
    """

    # The code to be tested
    if default is _OMIT_ARG:
        act_value = obj.get(key)
    else:
        act_value = obj.get(key, default)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_value == exp_value, \
        "Unexpected value at key {k!r} with default {d}". \
        format(k=key, d="omitted" if default is _OMIT_ARG else repr(default))


TESTCASES_DICTVIEW_ITEMS = [

    # Testcases for DictView.keys(), values(), items()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    #   * exp_items: List with expected items (key,value) in expected order.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
            exp_items=[],
        ),
        None, None, True
    ),
    (
        "Dict with one item",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat')])),
            exp_items=[('Dog', 'Cat')],
        ),
        None, None, True
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
            exp_items=[('Dog', 'Cat'), ('Budgie', 'Fish')],
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_keys(testcase, obj, exp_items):
    """
    Test function for DictView.keys()
    """

    # The code to be tested
    act_keys = obj.keys()

    # Also test iterating through the result
    act_keys_list = list(act_keys)

    # Test that second iteration is possible
    act_keys_list2 = list(act_keys)

    if not PY2:

        # Test __contained__() of the returned view
        for key in act_keys_list:
            assert key in act_keys

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    if PY2:
        assert isinstance(act_keys, list)
    else:
        assert isinstance(act_keys, KeysView)

    exp_keys = [item[0] for item in exp_items]
    assert act_keys_list == exp_keys
    assert act_keys_list2 == exp_keys


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_values(testcase, obj, exp_items):
    """
    Test function for DictView.values()
    """

    # The code to be tested
    act_values = obj.values()

    # Also test iterating through the result
    act_values_list = list(act_values)

    # Test that second iteration is possible
    act_values_list2 = list(act_values)

    if not PY2:

        # Test __contained__() of the returned view
        for value in act_values_list:
            assert value in act_values

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    if PY2:
        assert isinstance(act_values, list)
    else:
        assert isinstance(act_values, ValuesView)

    exp_values = [item[1] for item in exp_items]
    assert act_values_list == exp_values
    assert act_values_list2 == exp_values


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_items(testcase, obj, exp_items):
    """
    Test function for DictView.items()
    """

    # The code to be tested
    act_items = obj.items()

    # Also test iterating through the result
    act_items_list = list(act_items)

    # Test that second iteration is possible
    act_items_list2 = list(act_items)

    if not PY2:

        # Test __contained__() of the returned view
        for item in act_items_list:
            assert item in act_items

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    if PY2:
        assert isinstance(act_items, list)
    else:
        assert isinstance(act_items, ItemsView)

    assert act_items_list == exp_items
    assert act_items_list2 == exp_items


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_iterkeys(testcase, obj, exp_items):
    """
    Test function for DictView.iterkeys()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support iterkeys() method")

    assert PY2

    # The code to be tested
    act_keys = []
    for key in obj.iterkeys():
        act_keys.append(key)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    exp_keys = [item[0] for item in exp_items]
    assert act_keys == exp_keys


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_itervalues(testcase, obj, exp_items):
    """
    Test function for DictView.itervalues()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support itervalues() method")

    assert PY2

    # The code to be tested
    act_values = []
    for value in obj.itervalues():
        act_values.append(value)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    exp_values = [item[1] for item in exp_items]
    assert act_values == exp_values


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_iteritems(testcase, obj, exp_items):
    """
    Test function for DictView.iteritemss()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support iteritems() method")

    assert PY2

    # The code to be tested
    act_items = []
    for item in obj.iteritems():
        act_items.append(item)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert act_items == exp_items


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_viewkeys(testcase, obj, exp_items):
    """
    Test function for DictView.viewkeys() (PY2 only)
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does support viewkeys() method")

    assert PY2

    # The code to be tested
    act_keys = obj.viewkeys()

    # Also test iterating through the result
    act_keys_list = list(act_keys)

    # Test that second iteration is possible
    act_keys_list2 = list(act_keys)

    # Test __contained__() of the returned view
    for key in act_keys_list:
        assert key in act_keys

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert isinstance(act_keys, KeysView)

    exp_keys = [item[0] for item in exp_items]
    assert act_keys_list == exp_keys
    assert act_keys_list2 == exp_keys


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_viewvalues(testcase, obj, exp_items):
    """
    Test function for DictView.viewvalues()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support viewvalues() method")

    assert PY2

    # The code to be tested
    act_values = obj.viewvalues()

    # Also test iterating through the result
    act_values_list = list(act_values)

    # Test that second iteration is possible
    act_values_list2 = list(act_values)

    # Test __contained__() of the returned view
    for value in act_values_list:
        assert value in act_values

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert isinstance(act_values, ValuesView)

    exp_values = [item[1] for item in exp_items]
    assert act_values_list == exp_values
    assert act_values_list2 == exp_values


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_viewitems(testcase, obj, exp_items):
    """
    Test function for DictView.viewitems()
    """

    if not DICT_SUPPORTS_ITER_VIEW:
        pytest.skip("Test dictionary does not support viewitems() method")

    assert PY2

    # The code to be tested
    act_items = obj.viewitems()

    # Also test iterating through the result
    act_items_list = list(act_items)

    # Test that second iteration is possible
    act_items_list2 = list(act_items)

    # Test __contained__() of the returned view
    for item in act_items_list:
        assert item in act_items

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert isinstance(act_items, ItemsView)

    assert act_items_list == exp_items
    assert act_items_list2 == exp_items


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ITEMS)
@simplified_test_function
def test_DictView_iter(testcase, obj, exp_items):
    """
    Test function for DictView.__iter__() / for key in ncd
    """

    # The code to be tested
    act_keys = []
    for key in obj:
        act_keys.append(key)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    exp_keys = [item[0] for item in exp_items]
    assert act_keys == exp_keys


TESTCASES_DICTVIEW_REPR = [

    # Testcases for DictView.__repr__() / repr(ncd)

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj: DictView object to be used for the test.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            obj=DictView({}),
        ),
        None, None, True
    ),
    (
        "Dict with two items",
        dict(
            obj=DictView(OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_REPR)
@simplified_test_function
def test_DictView_repr(testcase, obj):
    """
    Test function for DictView.__repr__() / repr(ncd)
    """

    # The code to be tested
    result = repr(obj)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert re.match(r'^DictView\(.*\)$', result)

    # Note: This only tests for existence of each item, not for excess items
    # or representing the correct order.
    for item in obj.items():
        exp_item_result1 = "{0!r}: {1!r}".format(*item)
        exp_item_result2 = "({0!r}, {1!r})".format(*item)
        assert exp_item_result1 in result or exp_item_result2 in result


TESTCASES_DICTVIEW_COPY = [

    # Testcases for DictView.copy()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * dictview: DictView object to be used for the test.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "DictView with empty dict",
        dict(
            dictview=DictView({}),
        ),
        None, None, True
    ),
    (
        "DictView with dict with two items",
        dict(
            dictview=DictView(
                dict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
        ),
        None, None, True
    ),
    (
        "DictView with OrderedDict with two items",
        dict(
            dictview=DictView(
                OrderedDict([('Dog', 'Cat'), ('Budgie', 'Fish')])),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_COPY)
@simplified_test_function
def test_DictView_copy(testcase, dictview):
    """
    Test function for DictView.copy()
    """

    dictview_dict = dictview.dict

    # The code to be tested
    dictview_copy = dictview.copy()

    dictview_copy_dict = dictview_copy.dict

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # Verify the result type
    # pylint: disable=unidiomatic-typecheck
    assert type(dictview_copy) is DictView

    # Verify the result is a different object than the DictView
    assert id(dictview_copy) != id(dictview)

    # Verify the new dictionary is a different object than the underlying
    # dictionary, if mutable
    if isinstance(dictview_dict, MutableMapping):
        assert id(dictview_copy_dict) != id(dictview_dict)

    # Verify the new dictionary has the same type as the underlying dictionary
    # pylint: disable=unidiomatic-typecheck
    assert type(dictview_copy_dict) == type(dictview_dict)

    # Verify the new dictionary is equal to the underlying dictionary
    assert dictview_copy_dict == dictview_dict


TESTCASES_DICTVIEW_EQUAL = [

    # Testcases for DictView.__eq__(), __ne__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: DictView object #1 to use.
    #   * obj2: DictView object #2 to use.
    #   * exp_obj_equal: Expected equality of the objects.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dictionary",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "One item, keys and values equal",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([('k1', 'v1')])),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "One item, keys equal, values different",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([('k1', 'v1_x')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "One item, keys different, values equal",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([('k2', 'v1')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "One item, keys equal, values both None",
        dict(
            obj1=DictView(OrderedDict([('k1', None)])),
            obj2=DictView(OrderedDict([('k1', None)])),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Two equal items, in same order",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1'), ('k2', 'v2')])),
            obj2=DictView(OrderedDict([('k1', 'v1'), ('k2', 'v2')])),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Two equal items in ordered dict, in different order",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1'), ('k2', 'v2')])),
            obj2=DictView(OrderedDict([('k2', 'v2'), ('k1', 'v1')])),
            exp_obj_equal=False,
        ),
        None, None, False
    ),
    (
        "Two equal items in standard dict, in different order",
        dict(
            obj1=DictView(dict([('k1', 'v1'), ('k2', 'v2')])),
            obj2=DictView(dict([('k2', 'v2'), ('k1', 'v1')])),
            exp_obj_equal=not DICT_IS_ORDERED,
        ),
        None, None, False
    ),
    (
        "Comparing unicode value with bytes value",
        dict(
            obj1=DictView(OrderedDict([('k1', b'v1')])),
            obj2=DictView(OrderedDict([('k2', u'v2')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Matching unicode key with string key",
        dict(
            obj1=DictView(OrderedDict([('k1', 'v1')])),
            obj2=DictView(OrderedDict([(u'k2', 'v2')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Higher key missing",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Lower key missing",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Dog', 'Cat')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "First non-matching key is less. But longer size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([
                ('Budgie', 'Fish'), ('Curly', 'Snake'), ('Cozy', 'Dog'),
            ])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Only non-matching keys that are less. But longer size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict(
                [('Alf', 'F'), ('Anton', 'S'), ('Aussie', 'D')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "First non-matching key is greater. But shorter size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgio', 'Fish')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Only non-matching keys that are greater. But shorter size!",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Zoe', 'F')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size. First non-matching key is less",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict(
                [('Budgie', 'Fish'), ('Curly', 'Snake')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size. Only non-matching keys that are less",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Alf', 'F'), ('Anton', 'S')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size. Only non-matching keys that are greater",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Zoe', 'F'), ('Zulu', 'S')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size, only matching keys. First non-matching value is less",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Car')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "Same size, only matching keys. First non-matching value is greater",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Caz')])),
            exp_obj_equal=False,
        ),
        None, None, True
    ),
    (
        "A value raises TypeError when compared (and equal still succeeds)",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict(
                [('Budgie', NonEquatable()), ('Dog', 'Cat')])),
            exp_obj_equal=False,
        ),
        TypeError, None, True
    ),

    (
        "Mixing types: DictView of dict and dict",
        dict(
            obj1=DictView(dict({'Dog': 'Cat'})),
            obj2=dict({'Dog': 'Cat'}),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: dict and DictView of dict",
        dict(
            obj1=dict({'Dog': 'Cat'}),
            obj2=DictView(dict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: DictView of dict and OrDict (not a dict)",
        dict(
            obj1=DictView(dict({'Dog': 'Cat'})),
            obj2=OrDict({'Dog': 'Cat'}),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: dict and DictView of OrDict (not a dict)",
        dict(
            obj1=dict({'Dog': 'Cat'}),
            obj2=DictView(OrDict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: DictView of OrDict (not a dict) and DictView of dict",
        dict(
            obj1=DictView(OrDict({'Dog': 'Cat'})),
            obj2=DictView(dict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
    (
        "Mixing types: DictView of dict and DictView of OrDict (not a dict)",
        dict(
            obj1=DictView(dict({'Dog': 'Cat'})),
            obj2=DictView(OrDict({'Dog': 'Cat'})),
            exp_obj_equal=True,
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_EQUAL)
@simplified_test_function
def test_DictView_eq(testcase, obj1, obj2, exp_obj_equal):
    """
    Test function for DictView.__eq__() / ncd1==ncd2
    """

    # Double check they are different objects
    assert id(obj1) != id(obj2)

    # The code to be tested
    eq1 = (obj1 == obj2)
    eq2 = (obj2 == obj1)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert eq1 == exp_obj_equal
    assert eq2 == exp_obj_equal


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_EQUAL)
@simplified_test_function
def test_DictView_ne(testcase, obj1, obj2, exp_obj_equal):
    """
    Test function for DictView.__ne__() / ncd1!=ncd2
    """

    # Double check they are different objects
    assert id(obj1) != id(obj2)

    # The code to be tested
    ne1 = (obj1 != obj2)
    ne2 = (obj2 != obj1)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert ne1 != exp_obj_equal
    assert ne2 != exp_obj_equal


TESTCASES_DICTVIEW_ORDERING = [

    # Testcases for DictView.__le__(), __lt__(), __ge__(), __gt__() / ord.ops

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: DictView object #1 to be used.
    #   * obj2: DictView object #2 to be used.
    #   * op: Order comparison operator to be used, as a string (e.g. '>')
    #   * exp_result: Expected result of the comparison, or None.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    # Empty dicts
    (
        "Empty dicts with >",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='>',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Empty dicts with >=",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='>=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Empty dicts with <",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='<',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Empty dicts with <=",
        dict(
            obj1=DictView({}),
            obj2=DictView({}),
            op='<=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Equal dicts
    (
        "Equal dicts with >",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Equal dicts with >=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Equal dicts with <",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Equal dicts with <=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Dicts that compare less (obj1 < obj2)
    (
        "Less-comparing dicts with >",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Less-comparing dicts with >=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='>=',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Less-comparing dicts with <",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Less-comparing dicts with <=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            op='<=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Dicts that compare greater (obj1 > obj2)
    (
        "Greater-comparing dicts with >",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='>',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Greater-comparing dicts with >=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='>=',
            exp_result=True,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Greater-comparing dicts with <",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='<',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),
    (
        "Greater-comparing dicts with <=",
        dict(
            obj1=DictView(OrderedDict([('Budgie', 'Fish'), ('Dog', 'Cat')])),
            obj2=DictView(OrderedDict([('Budgie', 'Fish')])),
            op='<=',
            exp_result=False,
        ),
        None if DICT_SUPPORTS_COMPARISON else TypeError, None, True
    ),

    # Note: More subtle cases of less- or greater-comparing dicts are not
    # tested because the ordering comparison for DictView is deprecated.
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_ORDERING)
@simplified_test_function
def test_DictView_ordering(testcase, obj1, obj2, op, exp_result):
    """
    Test function for DictView.__le__(), __lt__(), __ge__(), __gt__() / ord.
    """

    comp_str = 'obj1 %s obj2' % op

    # Double check they are different objects
    assert id(obj1) != id(obj2)

    # The code to be tested
    result = eval(comp_str)  # pylint: disable=eval-used

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert result == exp_result


TESTCASES_DICTVIEW_PICKLE = [

    # Testcases for pickling and unpickling DictView objects

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * dictview: DictView object to be used for the test.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "DictView of empty dict",
        dict(
            dictview=DictView({}),
        ),
        None, None, True
    ),
    (
        "DictView of dict with one item",
        dict(
            dictview=DictView({'Dog': 'Cat'}),
        ),
        None, None, True
    ),
    (
        "DictView of DerivedDict with one item",
        dict(
            dictview=DictView(DerivedDict({'Dog': 'Cat'})),
        ),
        None, None, True
    ),
    (
        "DerivedDictView of dict with one item",
        dict(
            dictview=DerivedDictView(dict({'Dog': 'Cat'})),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_PICKLE)
@simplified_test_function
def test_DictView_pickle(testcase, dictview):
    """
    Test function for pickling and unpickling DictView objects
    """

    # Pickle the object
    pkl = pickle.dumps(dictview)

    # Unpickle the object
    dictview2 = pickle.loads(pkl)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert dictview2 == dictview


TESTCASES_DICTVIEW_HASH = [

    # Testcases for DictView.__hash__()

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * dict_obj: Underlying dictionary object.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "Empty dict",
        dict(
            dict_obj={},
        ),
        TypeError, None, True
    ),
    (
        "Empty OrderedDict",
        dict(
            dict_obj=OrderedDict(),
        ),
        TypeError, None, True
    ),
    (
        "Empty HashableDict",
        dict(
            dict_obj=HashableDict(),
        ),
        None, None, True
    ),

    (
        "Dict with one item",
        dict(
            dict_obj=dict([('a', 1)]),
        ),
        TypeError, None, True
    ),
    (
        "OrderedDict with one item",
        dict(
            dict_obj=OrderedDict([('a', 1)]),
        ),
        TypeError, None, True
    ),
    (
        "HashableDict with one item",
        dict(
            dict_obj=HashableDict([('a', 1)]),
        ),
        None, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_HASH)
@simplified_test_function
def test_DictView_hash(testcase, dict_obj):
    """
    Test function for DictView.__hash__() / hash()
    """

    view = DictView(dict_obj)

    # The code to be tested
    view_hash = hash(view)

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    # If it worked for the view, it must work for the underlying collection
    dict_hash = hash(dict_obj)

    # Verify the hash value of the underlying collection is used for the view
    assert view_hash == dict_hash


TESTCASES_DICTVIEW_OR = [

    # Testcases for DictView.__or__/__ror__() / self | or

    # Each list item is a testcase tuple with these items:
    # * desc: Short testcase description.
    # * kwargs: Keyword arguments for the test function:
    #   * obj1: DictView object #1 to be used.
    #   * obj2: DictView object #2 to be used.
    #   * exp_result: Expected result of the operation, or None.
    # * exp_exc_types: Expected exception type(s), or None.
    # * exp_warn_types: Expected warning type(s), or None.
    # * condition: Boolean condition for testcase to run, or 'pdb' for debugger

    (
        "DictView of empty NorDict and DictView of empty NorDict",
        dict(
            obj1=DictView(NorDict()),
            obj2=DictView(NorDict()),
            exp_result=None,
        ),
        TypeError, None, True
    ),
    (
        "DictView of empty OrDict and DictView of empty NorDict",
        dict(
            obj1=DictView(OrDict()),
            obj2=DictView(NorDict()),
            exp_result=DictView(OrDict()),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "DictView of empty NorDict and DictView of empty OrDict",
        dict(
            obj1=DictView(NorDict()),
            obj2=DictView(OrDict()),
            exp_result=DictView(NorDict()),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "Empty NorDict and DictView of empty OrDict",
        dict(
            obj1=NorDict(),
            obj2=DictView(OrDict()),
            exp_result=NorDict(),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with two items and NorDict with equal two items",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with two items and OrDict with equal two items",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "Empty OrDict and NorDict with two items",
        dict(
            obj1=DictView(OrDict()),
            obj2=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "Empty NorDict and OrDict with two items",
        dict(
            obj1=DictView(NorDict()),
            obj2=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with two items and empty NorDict",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(NorDict()),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with two items and empty OrDict",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
            obj2=DictView(OrDict()),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with one item and NorDict with one item with different key",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish'})),
            obj2=DictView(NorDict({'Dog': 'Cat'})),
            exp_result=DictView(OrDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with one item and OrDict with one item with different key",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish'})),
            obj2=DictView(OrDict({'Dog': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Fish', 'Dog': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),

    (
        "OrDict with one item and NorDict with one item with same key, "
        "different value",
        dict(
            obj1=DictView(OrDict({'Budgie': 'Fish'})),
            obj2=DictView(NorDict({'Budgie': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
    (
        "NorDict with one item and OrDict with one item with same key, "
        "different value",
        dict(
            obj1=DictView(NorDict({'Budgie': 'Fish'})),
            obj2=DictView(OrDict({'Budgie': 'Cat'})),
            exp_result=DictView(NorDict({'Budgie': 'Cat'})),
        ),
        None if DICT_SUPPORTS_OR else TypeError, None, True
    ),
]


@pytest.mark.parametrize(
    "desc, kwargs, exp_exc_types, exp_warn_types, condition",
    TESTCASES_DICTVIEW_OR)
@simplified_test_function
def test_DictView_or(testcase, obj1, obj2, exp_result):
    """
    Test function for DictView.__or__/__ror__() / self | or
    """

    # The code to be tested
    result = obj1 | obj2

    # Ensure that exceptions raised in the remainder of this function
    # are not mistaken as expected exceptions
    assert testcase.exp_exc_types is None

    assert result == exp_result

# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # #    http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Test the DictView class. # pylint: disable=deprecated-class # pylint: disable=wrong-import-position, wrong-import-order, invalid-name # noqa: E402 # pylint: enable=wrong-import-position, wrong-import-order, invalid-name Hashable dictionary, for testing Dictionary without OR operator, for testing Dictionary with OR operator, for testing Dictionary derived from dict with additional attribute Dictionary view derived from DictView with additional attribute # Indicates Python dict supports lt/gt comparison (between dicts) # Indicates Python dict is ordered # Indicates Python dict issues UserWarning about not preserving order # of items in kwargs or in standard dict # Indicates Python dict supports the iter..() and view..() methods # Indicates Python dict supports the has_key() method # Indicates Python dict supports the __reversed__() method # Indicates Python dict supports the __or__/__ror__() methods # Used as indicator not to pass an argument in the testcases. # Note this has nothing to do with the _OMITTED flag in _immutable_views.py and # could be a different value. # pylint: disable=too-few-public-methods Class that raises TypeError when comparing its objects for equality. # Testcases for DictView.__init__() / ncd=DictView() # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * init_args: Tuple of positional arguments to DictView(). #   * init_kwargs: Dict of keyword arguments to DictView(). #   * exp_dict: Expected resulting dictionary, as OrderedDict. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger # Empty underlying dictionaries # Non-empty underlying dictionaries # Error cases with inputs that are invalid for the view but would be valid # for a real dictionary # Other error cases Test function for DictView.__init__() / ncd=DictView() # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # The verification below also uses some DictView features, but that is # unavoidable if we want to work through the public interface: # Uses DictView iteration # Uses DictView getitem # Next line uses DictView contains: # Uses DictView getitem # Testcases for DictView.__getitem__() / ncd[key] # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. #   * key: Key to be used for the test. #   * exp_value: Expected value for the key. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger # Empty underlying dictionary # Non-empty underlying dictionary Test function for DictView.__getitem__() / ncd[key] # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.__len__() / len(ncd) # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. #   * exp_len: Expected len() value. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.__len__() / len(ncd) # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.__contains__() / key in ncd # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. #   * key: Key to be used for the test. #   * exp_result: Expected result (bool). # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger # Empty DictView # Non-empty DictView Test function for DictView.__contains__() / key in ncd # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.has_key() # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. #   * key: Key to be used for the test. #   * exp_result: Expected result (bool). # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger # Empty DictView # Non-empty DictView Test function for DictView.has_key() # The code to be tested # noqa: W601 # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.__reversed__() / reversed(ncd) # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. #   * exp_keys: Expected result as a list of keys, or None. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.__reversed__() / reversed(ncd) # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # The reason we verify that an iterator is returned is that # DictView.__reversed__() delegates to keys() which returns a list in # Python 2, so this verifies that reversed() still turns this into an # iterator. # Testcases for DictView.get() # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. #   * key: Key to be used for the test. #   * default: Default value to be used for the test, or _OMIT_ARG. #   * exp_value: Expected value at the key. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger # Empty DictView # Non-empty DictView Test function for DictView.get() # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.keys(), values(), items() # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. #   * exp_items: List with expected items (key,value) in expected order. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.keys() # The code to be tested # Also test iterating through the result # Test that second iteration is possible # Test __contained__() of the returned view # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.values() # The code to be tested # Also test iterating through the result # Test that second iteration is possible # Test __contained__() of the returned view # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.items() # The code to be tested # Also test iterating through the result # Test that second iteration is possible # Test __contained__() of the returned view # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.iterkeys() # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.itervalues() # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.iteritemss() # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.viewkeys() (PY2 only) # The code to be tested # Also test iterating through the result # Test that second iteration is possible # Test __contained__() of the returned view # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.viewvalues() # The code to be tested # Also test iterating through the result # Test that second iteration is possible # Test __contained__() of the returned view # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.viewitems() # The code to be tested # Also test iterating through the result # Test that second iteration is possible # Test __contained__() of the returned view # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.__iter__() / for key in ncd # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.__repr__() / repr(ncd) # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj: DictView object to be used for the test. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.__repr__() / repr(ncd) # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Note: This only tests for existence of each item, not for excess items # or representing the correct order. # Testcases for DictView.copy() # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * dictview: DictView object to be used for the test. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.copy() # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Verify the result type # pylint: disable=unidiomatic-typecheck # Verify the result is a different object than the DictView # Verify the new dictionary is a different object than the underlying # dictionary, if mutable # Verify the new dictionary has the same type as the underlying dictionary # pylint: disable=unidiomatic-typecheck # Verify the new dictionary is equal to the underlying dictionary # Testcases for DictView.__eq__(), __ne__() # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj1: DictView object #1 to use. #   * obj2: DictView object #2 to use. #   * exp_obj_equal: Expected equality of the objects. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.__eq__() / ncd1==ncd2 # Double check they are different objects # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions Test function for DictView.__ne__() / ncd1!=ncd2 # Double check they are different objects # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.__le__(), __lt__(), __ge__(), __gt__() / ord.ops # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj1: DictView object #1 to be used. #   * obj2: DictView object #2 to be used. #   * op: Order comparison operator to be used, as a string (e.g. '>') #   * exp_result: Expected result of the comparison, or None. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger # Empty dicts # Equal dicts # Dicts that compare less (obj1 < obj2) # Dicts that compare greater (obj1 > obj2) # Note: More subtle cases of less- or greater-comparing dicts are not # tested because the ordering comparison for DictView is deprecated. Test function for DictView.__le__(), __lt__(), __ge__(), __gt__() / ord. # Double check they are different objects # The code to be tested # pylint: disable=eval-used # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for pickling and unpickling DictView objects # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * dictview: DictView object to be used for the test. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for pickling and unpickling DictView objects # Pickle the object # Unpickle the object # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # Testcases for DictView.__hash__() # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * dict_obj: Underlying dictionary object. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.__hash__() / hash() # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions # If it worked for the view, it must work for the underlying collection # Verify the hash value of the underlying collection is used for the view # Testcases for DictView.__or__/__ror__() / self | or # Each list item is a testcase tuple with these items: # * desc: Short testcase description. # * kwargs: Keyword arguments for the test function: #   * obj1: DictView object #1 to be used. #   * obj2: DictView object #2 to be used. #   * exp_result: Expected result of the operation, or None. # * exp_exc_types: Expected exception type(s), or None. # * exp_warn_types: Expected warning type(s), or None. # * condition: Boolean condition for testcase to run, or 'pdb' for debugger Test function for DictView.__or__/__ror__() / self | or # The code to be tested # Ensure that exceptions raised in the remainder of this function # are not mistaken as expected exceptions